flutter: Refactor a lot

This commit is contained in:
Calcitem 2020-11-21 00:53:18 +08:00
parent beee9a0cef
commit 696095724a
26 changed files with 362 additions and 728 deletions

View File

@ -20,7 +20,6 @@
abs(value) => value > 0 ? value : -value;
int binarySearch(List<int> array, int start, int end, int key) {
//
if (start > end) return -1;
if (array[start] == key) return start;

View File

@ -20,29 +20,26 @@
import 'profile.dart';
class Config {
//
static bool bgmEnabled = false;
static bool toneEnabled = true;
static int stepTime = 5000;
static int thinkingTime = 5000;
static Future<void> loadProfile() async {
//
final profile = await Profile.shared();
Config.bgmEnabled = profile['bgm-enabled'] ?? false;
Config.toneEnabled = profile['tone-enabled'] ?? true;
Config.stepTime = profile['step-time'] ?? 5000;
Config.thinkingTime = profile['thinking-time'] ?? 5000;
return true;
}
static Future<bool> save() async {
//
final profile = await Profile.shared();
profile['bgm-enabled'] = Config.bgmEnabled;
profile['tone-enabled'] = Config.toneEnabled;
profile['step-time'] = Config.stepTime;
profile['thinking-time'] = Config.thinkingTime;
profile.commit();

View File

@ -23,18 +23,16 @@ import 'dart:io';
import 'package:path_provider/path_provider.dart';
class Profile {
//
static const DefaultFileName = 'default-profile.json';
static const defaultFileName = 'default-profile.json';
static Profile _shared;
File _file;
Map<String, dynamic> _values = {};
static shared() async {
//
if (_shared == null) {
_shared = Profile();
await _shared._load(DefaultFileName);
await _shared._load(defaultFileName);
}
return _shared;
@ -45,7 +43,6 @@ class Profile {
operator []=(String key, dynamic value) => _values[key] = value;
Future<bool> commit() async {
//
_file.create(recursive: true);
try {
@ -60,7 +57,6 @@ class Profile {
}
Future<bool> _load(String fileName) async {
//
final docDir = await getApplicationDocumentsDirectory();
_file = File('${docDir.path}/$fileName');

View File

@ -17,41 +17,37 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class AnalysisItem {
//
String move, stepName;
class AnalyzeItem {
String move, moveName;
int score;
double winrate;
double winRate;
AnalysisItem({this.move, this.score, this.winrate});
AnalyzeItem({this.move, this.score, this.winRate});
@override
String toString() {
return '{move: ${stepName ?? move}, score: $score, winrate: $winrate}';
return '{move: ${moveName ?? move}, score: $score, winRate: $winRate}';
}
}
class AnalysisFetcher {
//
static List<AnalysisItem> fetch(String response, {limit = 5}) {
//
class AnalyzeFetcher {
static List<AnalyzeItem> fetch(String response, {limit = 5}) {
final segments = response.split('|');
List<AnalysisItem> result = [];
List<AnalyzeItem> result = [];
final regx = RegExp(r'move:(.{4}).+score:(\-?\d+).+winrate:(\d+.?\d*)');
final regExp = RegExp(r'move:(.{4}).+score:(\-?\d+).+winRate:(\d+.?\d*)');
for (var segment in segments) {
//
final match = regx.firstMatch(segment);
final match = regExp.firstMatch(segment);
if (match == null) break;
final move = match.group(1);
final score = int.parse(match.group(2));
final winrate = double.parse(match.group(3));
final winRate = double.parse(match.group(3));
result.add(AnalysisItem(move: move, score: score, winrate: winrate));
result.add(AnalyzeItem(move: move, score: score, winRate: winRate));
if (result.length == limit) break;
}

View File

@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import '../mill/position.dart';
import 'package:sanmill/mill/position.dart';
enum EngineType { Cloud, Native }
@ -28,10 +28,7 @@ class EngineResponse {
}
abstract class AiEngine {
//
Future<void> startup() async {}
Future<void> shutdown() async {}
Future<EngineResponse> search(Position position, {bool byUser = true});
}

View File

@ -18,30 +18,25 @@
*/
import 'package:flutter/services.dart';
import 'package:sanmill/mill/mill.dart';
import 'package:sanmill/mill/position.dart';
import '../mill/mill.dart';
import '../mill/position.dart';
import 'engine.dart';
class NativeEngine extends AiEngine {
//
static const platform = const MethodChannel('com.calcitem.sanmill/engine');
Future<void> startup() async {
//
try {
await platform.invokeMethod('startup');
} catch (e) {
print('Native startup Error: $e');
}
//await setBookFile();
await waitResponse(['uciok'], sleep: 1, times: 30);
}
Future<void> send(String command) async {
//
try {
print("send: $command");
await platform.invokeMethod('send', command);
@ -51,7 +46,6 @@ class NativeEngine extends AiEngine {
}
Future<String> read() async {
//
try {
return await platform.invokeMethod('read');
} catch (e) {
@ -62,7 +56,6 @@ class NativeEngine extends AiEngine {
}
Future<void> shutdown() async {
//
try {
await platform.invokeMethod('shutdown');
} catch (e) {
@ -71,7 +64,6 @@ class NativeEngine extends AiEngine {
}
Future<bool> isReady() async {
//
try {
return await platform.invokeMethod('isReady');
} catch (e) {
@ -82,7 +74,6 @@ class NativeEngine extends AiEngine {
}
Future<bool> isThinking() async {
//
try {
return await platform.invokeMethod('isThinking');
} catch (e) {
@ -92,32 +83,11 @@ class NativeEngine extends AiEngine {
return null;
}
/*
Future setBookFile() async {
//
final docDir = await getApplicationDocumentsDirectory();
final bookFile = File('${docDir.path}/book.dat');
try {
if (!await bookFile.exists()) {
await bookFile.create(recursive: true);
final bytes = await rootBundle.load("assets/book.dat");
await bookFile.writeAsBytes(bytes.buffer.asUint8List());
}
} catch (e) {
print(e);
}
await send("setoption bookfiles ${bookFile.path}");
}
*/
@override
Future<EngineResponse> search(Position position, {bool byUser = true}) async {
//
if (await isThinking()) await stopSearching();
send(buildPositionCommand(position));
send(getPositionFen(position));
send('go');
final response = await waitResponse(['bestmove', 'nobestmove']);
@ -125,13 +95,12 @@ class NativeEngine extends AiEngine {
print("response: $response");
if (response.startsWith('bestmove')) {
//
var step = response.substring('bestmove'.length + 1);
var best = response.substring('bestmove'.length + 1);
final pos = step.indexOf(' ');
if (pos > -1) step = step.substring(0, pos);
final pos = best.indexOf(' ');
if (pos > -1) best = best.substring(0, pos);
return EngineResponse('move', value: Move.fromEngineMove(step));
return EngineResponse('move', value: Move.set(best));
}
if (response.startsWith('nobestmove')) {
@ -143,7 +112,6 @@ class NativeEngine extends AiEngine {
Future<String> waitResponse(List<String> prefixes,
{sleep = 100, times = 100}) async {
//
if (times <= 0) return '';
final response = await read();
@ -164,7 +132,7 @@ class NativeEngine extends AiEngine {
await send('stop');
}
String buildPositionCommand(Position position) {
String getPositionFen(Position position) {
/*
final startPosition = position.lastCapturedPosition;
final moves = position.movesSinceLastCaptured();

View File

@ -22,9 +22,9 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import './routes/main_menu.dart';
import 'services/audios.dart';
import 'services/player.dart';
import 'widgets/main_menu.dart';
void main() {
//

View File

@ -17,14 +17,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:sanmill/common/types.dart';
import 'package:sanmill/mill/types.dart';
import '../mill/mill.dart';
import '../mill/position.dart';
import 'mill.dart';
import 'position.dart';
class Battle {
//
static Battle _instance;
class Game {
static Game _instance;
Position _position;
int _focusIndex, _blurIndex;
@ -32,21 +31,16 @@ class Battle {
String sideToMove = Color.black;
//
bool isInverted;
bool isColorInverted;
Map<String, bool> isAiPlayer = {Color.black: false, Color.white: true};
Map<String, bool> isAiSearching = {Color.black: false, Color.white: false};
Battle() {
//cmdlist = new List;
}
Map<String, bool> isAi = {Color.black: false, Color.white: true};
Map<String, bool> isSearching = {Color.black: false, Color.white: false};
bool aiIsSearching() {
return isAiSearching[Color.black] == true ||
isAiSearching[Color.white] == true;
return isSearching[Color.black] == true || isSearching[Color.white] == true;
}
void gameStart() {
void start() {
position.start();
}
@ -60,7 +54,7 @@ class Battle {
static bool hasSound = true;
//
bool resignIfMostLose_ = false;
bool resignIfMostLose = false;
//
bool isAutoChangeFirstMove = false;
@ -74,34 +68,34 @@ class Battle {
//
String tips;
List<String> cmdlist = [""];
List<String> moveHistory = [""];
String getTips() => tips;
bool isAIsTurn() {
return isAiPlayer[sideToMove];
bool isAiToMove() {
return isAi[sideToMove];
}
static get shared {
_instance ??= Battle();
_instance ??= Game();
return _instance;
}
init() {
_position = Position();
_focusIndex = _blurIndex = Move.invalidValue;
_focusIndex = _blurIndex = Move.invalidMove;
}
newGame() {
Battle.shared.position.init();
_focusIndex = _blurIndex = Move.invalidValue;
cmdlist = [""];
Game.shared.position.init();
_focusIndex = _blurIndex = Move.invalidMove;
moveHistory = [""];
sideToMove = Color.black;
}
select(int pos) {
_focusIndex = pos;
_blurIndex = Move.invalidValue;
_blurIndex = Move.invalidMove;
//Audios.playTone('click.mp3');
}
@ -112,30 +106,23 @@ class Battle {
_blurIndex = from;
_focusIndex = to;
/*
if (ChessRules.checked(position)) {
//Audios.playTone('check.mp3');
} else {
//Audios.playTone(captured != Piece.Empty ? 'capture.mp3' : 'move.mp3');
}
*/
return true;
}
bool regret({steps = 2}) {
bool regret({moves = 2}) {
//
//
// TODO
if (_position.side != Color.white) {
//Audios.playTone('invalid.mp3');
return false;
}
var regreted = false;
var regretted = false;
///
for (var i = 0; i < steps; i++) {
for (var i = 0; i < moves; i++) {
//
if (!_position.regret()) break;
@ -148,13 +135,13 @@ class Battle {
//
} else {
//
_blurIndex = _focusIndex = Move.invalidValue;
_blurIndex = _focusIndex = Move.invalidMove;
}
regreted = true;
regretted = true;
}
if (regreted) {
if (regretted) {
//Audios.playTone('regret.mp3');
return true;
}
@ -164,7 +151,7 @@ class Battle {
}
clear() {
_blurIndex = _focusIndex = Move.invalidValue;
_blurIndex = _focusIndex = Move.invalidMove;
}
GameResult scanBattleResult() {
@ -196,12 +183,12 @@ class Battle {
//
if (position.phase == Phase.ready) {
gameStart();
start();
}
print("Computer: $cmd");
cmdlist.add(cmd);
moveHistory.add(cmd);
if (!position.command(cmd)) {
return false;
@ -209,30 +196,6 @@ class Battle {
sideToMove = position.sideToMove();
String winner = position.winner;
if (winner != Color.nobody) {
//resumeAiThreads(position.sideToMove());
// TODO
} else {
// pauseThreads();
/*
if (gameOptions.getAutoRestart()) {
saveScore();
gameReset();
gameStart();
if (isAiPlayer[BLACK]) {
setEngine(BLACK, true);
}
if (isAiPlayer[WHITE]) {
setEngine(WHITE, true);
}
}
*/
}
total = position.score[Color.black] +
position.score[Color.white] +
position.score[Color.draw];
@ -247,7 +210,7 @@ class Battle {
drawRate = position.score[Color.draw] * 100 / total;
}
String outStr = "Score: " +
String stat = "Score: " +
position.score[Color.black].toString() +
" : " +
position.score[Color.white].toString() +
@ -264,7 +227,7 @@ class Battle {
"%" +
"\n";
print(outStr);
print(stat);
return true;
}
}

View File

@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:sanmill/common/types.dart';
import 'types.dart';
Map<int, int> squareToIndex = {
8: 17,
@ -56,7 +56,6 @@ int makeSquare(int file, int rank) {
enum GameResult { pending, win, lose, draw }
class Color {
//
static const none = '*';
static const black = '@';
static const white = 'O';
@ -85,24 +84,19 @@ class Color {
}
class Piece {
//
static const noPiece = '*';
//
static const blackStone = '@';
static const whiteStone = 'O';
static const ban = 'X';
static const noPiece = Color.none;
static const blackStone = Color.black;
static const whiteStone = Color.white;
static const ban = Color.ban;
static bool isBlack(String c) => '@'.contains(c);
static bool isWhite(String c) => 'O'.contains(c);
static bool isBan(String c) => 'X'.contains(c);
static bool isEmpty(String c) => '*'.contains(c);
static bool isEmpty(String c) => noPiece.contains(c);
static bool isBlack(String c) => blackStone.contains(c);
static bool isWhite(String c) => whiteStone.contains(c);
static bool isBan(String c) => ban.contains(c);
}
class Move {
static const invalidValue = -1;
static const invalidMove = -1;
// Square
int from = 0;
@ -118,7 +112,7 @@ class Move {
int fromIndex = 0;
int toIndex = 0;
String captured;
String removed;
// 'move' is the UCI engine's move-string
String move;
@ -129,13 +123,13 @@ class Move {
String counterMarks;
parse() {
if (!validateEngineMove(move)) {
if (!legal(move)) {
throw "Error: Invalid Move: $move";
}
if (move[0] == '-' && move.length == "-(1,2)".length) {
type = MoveType.remove;
from = fromFile = fromRank = fromIndex = invalidValue;
from = fromFile = fromRank = fromIndex = invalidMove;
toFile = int.parse(move[2]);
toRank = int.parse(move[4]);
//captured = Piece.noPiece;
@ -147,13 +141,13 @@ class Move {
fromIndex = squareToIndex[from];
toFile = int.parse(move[8]);
toRank = int.parse(move[10]);
captured = Piece.noPiece;
removed = Piece.noPiece;
} else if (move.length == "(1,2)".length) {
type = MoveType.place;
from = fromFile = fromRank = fromIndex = invalidValue;
from = fromFile = fromRank = fromIndex = invalidMove;
toFile = int.parse(move[1]);
toRank = int.parse(move[3]);
captured = Piece.noPiece;
removed = Piece.noPiece;
} else if (move == "draw") {
// TODO
print("Computer request draw");
@ -169,44 +163,24 @@ class Move {
parse();
}
/*
Move(this.from, this.to,
{this.captured = Piece.noPiece, this.counterMarks = '0 0'}) {
//
fx = from % 9;
fy = from ~/ 9;
tx = to % 9;
ty = to ~/ 9;
if (fx < 0 || fx > 8 || fy < 0 || fy > 9) {
throw "Error: Invalid Step (from:$from, to:$to)";
}
move = String.fromCharCode('a'.codeUnitAt(0) + fx) + (9 - fy).toString();
move += String.fromCharCode('a'.codeUnitAt(0) + tx) + (9 - ty).toString();
}
*/
/// :
/// (1,2)
/// -(1,2)
/// (3,1)->(2,1)
Move.fromEngineMove(String move) {
//
Move.set(String move) {
this.move = move;
parse();
}
static bool validateEngineMove(String move) {
static bool legal(String move) {
if (move == "draw") {
return true; // TODO
}
if (move == null || move.length > "(3,1)->(2,1)".length) return false;
String sets = "0123456789(,)->";
String range = "0123456789(,)->";
if (!(move[0] == '(' || move[0] == '-')) {
return false;
@ -217,7 +191,13 @@ class Move {
}
for (int i = 0; i < move.length; i++) {
if (!sets.contains(move[i])) return false;
if (!range.contains(move[i])) return false;
}
if (move.length == "(3,1)->(2,1)".length) {
if (move.substring(0, 4) == move.substring(7, 11)) {
return false;
}
}
return true;

View File

@ -18,11 +18,11 @@
*/
import 'package:flutter/cupertino.dart';
import 'package:sanmill/mill/mill.dart';
import 'package:sanmill/mill/recorder.dart';
import 'package:sanmill/mill/rule.dart';
import '../common/types.dart';
import '../mill/mill.dart';
import '../mill/recorder.dart';
import '../mill/rule.dart';
import 'types.dart';
class StateInfo {
/*
@ -44,10 +44,10 @@ class Position {
GameResult result = GameResult.pending;
List<String> board = List<String>(40);
List<String> _grid = List<String>(49); // 7 * 7
List<String> board = List<String>(sqNumber);
List<String> _grid = List<String>(7 * 7);
MillRecorder _recorder;
GameRecorder _recorder;
Map<String, int> pieceCountInHand = {Color.black: 12, Color.white: 12};
Map<String, int> pieceCountOnBoard = {Color.black: 0, Color.white: 0};
@ -64,6 +64,7 @@ class Position {
String us = Color.black;
String them = Color.white;
String winner = Color.nobody;
GameOverReason gameOverReason = GameOverReason.noReason;
Phase phase = Phase.none;
@ -145,57 +146,9 @@ class Position {
return pieceOn(fromSq(move));
}
bool selectPieceFR(int file, int rank) {
return selectPieceSQ(makeSquare(file, rank));
}
bool putPieceFR(int file, int rank) {
bool ret = putPieceSQ(makeSquare(file, rank));
if (ret) {
updateScore();
}
return ret;
}
bool movePieceFR(int file1, int rank1, int file2, int rank2) {
return movePieceSQ(makeSquare(file1, rank1), makeSquare(file2, rank2));
}
bool removePieceFR(int file, int rank) {
bool ret = removePieceSQ(makeSquare(file, rank));
if (ret) {
updateScore();
}
return ret;
}
bool selectPieceSQ(int sq) {
// TODO
return false;
}
bool putPieceSQ(int sq) {
// TODO
return false;
}
bool movePieceSQ(int fromSq, int toSq) {
// TODO
return false;
}
bool removePieceSQ(int sq) {
// TODO
return false;
}
bool movePiece(int fromSq, int toSq) {
if (selectPiece(fromSq)) {
return putPiece(toSq);
bool movePiece(int from, int to) {
if (selectPiece(from)) {
return putPiece(to);
}
return false;
@ -219,7 +172,7 @@ class Position {
// TODO
_recorder = MillRecorder(lastCapturedPosition: fen());
_recorder = GameRecorder(lastPositionWithRemove: fen());
}
Position() {
@ -227,41 +180,6 @@ class Position {
init();
}
void set(String fenStr) {
/*
A FEN string defines a particular position using only the ASCII character set.
A FEN string contains six fields separated by a space. The fields are:
1) Piece placement. Each rank is described, starting
with rank 1 and ending with rank 8. Within each rank, the contents of each
square are described from file A through file C. Following the Standard
Algebraic Notation (SAN), each piece is identified by a single letter taken
from the standard English names. White pieces are designated using "O"
whilst Black uses "@". Blank uses "*". Banned uses "X".
noted using digits 1 through 8 (the number of blank squares), and "/"
separates ranks.
2) Active color. "w" means white moves next, "b" means black.
3) Phrase.
4) Action.
5) Black on board/Black in hand/White on board/White in hand/need to remove
6) Halfmove clock. This is the number of halfmoves since the last
capture. This is used to determine if a draw can be claimed under the
fifty-move rule.
7) Fullmove number. The number of the full move. It starts at 1, and is
incremented after Black's move.
*/
// TODO
return;
}
/// fen() returns a FEN representation of the position.
String fen() {
@ -354,19 +272,19 @@ class Position {
/// Position::legal() tests whether a pseudo-legal move is legal
bool legal(int move) {
assert(isOk(move));
bool legal(Move move) {
if (!isOk(move.from) || !isOk(move.to)) return false;
String us = _sideToMove;
int fromSQ = fromSq(move);
int toSQ = toSq(move);
if (fromSQ == toSQ) {
if (move.from == move.to) {
print("Move $move.move from == to");
return false; // TODO: Same with is_ok(m)
}
if (phase == Phase.moving && typeOf(move) != MoveType.remove) {
if (movedPiece(move) != us) {
if (move.type == MoveType.remove) {
if (movedPiece(move.to) != us) {
print("Move $move.to to != us");
return false;
}
}
@ -376,20 +294,10 @@ class Position {
return true;
}
/// Position::pseudo_legal() takes a random move and tests whether the move is
/// pseudo legal. It is used to validate moves from TT that can be corrupted
/// due to SMP concurrent access or hash position key aliasing.
bool pseudoLegal(int move) {
// TODO
return legal(move);
}
void doMove(Move m) {
//
//if (!validateMove(m)) return null;
//final move = Move(m);
if (!legal(m)) {
return null;
}
bool ret = false;
@ -421,22 +329,11 @@ class Position {
this.move = m;
//StepName.translate(this, move);
_recorder.stepIn(move, this);
//
//_sideToMove = Color.opponent(_sideToMove);
}
void doNullMove() {
changeSideToMove();
}
void undoNullMove() {
changeSideToMove();
_recorder.moveIn(move, this);
}
bool posIsOk() {
// TODO
return true;
}
@ -445,9 +342,9 @@ class Position {
int piecesOnBoardCount() {
pieceCountOnBoard[Color.black] = pieceCountOnBoard[Color.white] = 0;
for (int f = 1; f < 3 + 2; f++) {
for (int r = 0; r < 8; r++) {
int s = f * 8 + r;
for (int f = 1; f < fileExNumber; f++) {
for (int r = 0; r < rankNumber; r++) {
int s = f * rankNumber + r;
if (board[s] == Piece.blackStone) {
pieceCountOnBoard[Color.black]++;
} else if (board[s] == Piece.whiteStone) {
@ -473,6 +370,16 @@ class Position {
return pieceCountOnBoard[Color.black] + pieceCountOnBoard[Color.white];
}
void clearBoard() {
for (int i = 0; i < _grid.length; i++) {
_grid[i] = Piece.noPiece;
}
for (int i = 0; i < board.length; i++) {
board[i] = Piece.noPiece;
}
}
int setPosition(Rule newRule) {
result = GameResult.pending;
@ -490,13 +397,7 @@ class Position {
cmdline = "";
for (int i = 0; i < _grid.length; i++) {
_grid[i] = Piece.noPiece;
}
for (int i = 0; i < board.length; i++) {
board[i] = Piece.noPiece;
}
clearBoard();
if (piecesOnBoardCount() == -1) {
return -1;
@ -509,6 +410,7 @@ class Position {
createMoveTable();
createMillTable();
currentSquare = 0;
return -1;
}
@ -523,13 +425,7 @@ class Position {
winner = Color.nobody;
gameOverReason = GameOverReason.noReason;
for (int i = 0; i < _grid.length; i++) {
_grid[i] = Piece.noPiece;
}
for (int i = 0; i < board.length; i++) {
board[i] = Piece.noPiece;
}
clearBoard();
pieceCountOnBoard[Color.black] = pieceCountOnBoard[Color.white] = 0;
pieceCountInHand[Color.black] =
@ -761,13 +657,13 @@ class Position {
return true;
}
bool selectPiece(int s) {
bool selectPiece(int sq) {
if (phase != Phase.moving) return false;
if (action != Act.select && action != Act.place) return false;
if (board[s] == sideToMove()) {
currentSquare = s;
if (board[sq] == sideToMove()) {
currentSquare = sq;
action = Act.place;
return true;
@ -793,18 +689,19 @@ class Position {
bool command(String cmd) {
// TODO
/*
if (sscanf(cmd, "r%1u s%3d t%2u", &ruleIndex, &step, &t) == 3) {
if (ruleIndex <= 0 || ruleIndex > N_RULES) {
return false;
}
if (sscanf(cmd, "r%1u s%3d t%2u", &ruleIndex, &step, &t) == 3) {
if (ruleIndex <= 0 || ruleIndex > N_RULES) {
return false;
}
return set_position(&RULES[ruleIndex - 1]) >= 0 ? true : false;
}
return set_position(&RULES[ruleIndex - 1]) >= 0 ? true : false;
}
*/
print("position: command = $cmd");
if (cmd.length > 6 && cmd.substring(0, 5) == "Player") {
if (cmd[6] == '1') {
if (cmd.length > "Player".length &&
cmd.substring(0, "Player".length - 1) == "Player") {
if (cmd["Player".length] == '1') {
return resign(Color.black);
} else {
return resign(Color.white);
@ -1449,7 +1346,7 @@ class Position {
int inHowManyMills(int s, String c, {int squareSelected = 0}) {
int n = 0;
String locbak = Piece.noPiece;
String ptBak = Piece.noPiece;
assert(0 <= squareSelected && squareSelected < sqNumber);
@ -1458,20 +1355,19 @@ class Position {
}
if (squareSelected != 0) {
locbak = board[squareSelected];
ptBak = board[squareSelected];
board[squareSelected] =
_grid[squareToIndex[squareSelected]] = Piece.noPiece;
}
for (int l = 0; l < lineDirectionNumber; l++) {
// TODO: right?
if (c == board[millTable[s][l][0]] && c == board[millTable[s][l][1]]) {
n++;
}
}
if (squareSelected != 0) {
board[squareSelected] = _grid[squareToIndex[squareSelected]] = locbak;
board[squareSelected] = _grid[squareToIndex[squareSelected]] = ptBak;
}
return n;
@ -1490,7 +1386,6 @@ class Position {
idx[2] = millTable[s][i][1];
// no mill
// TODO: right?
if (!(m == board[idx[1]] && m == board[idx[2]])) {
continue;
}
@ -1571,45 +1466,37 @@ class Position {
///////////////////////////////////////////////////////////////////////////////
//
bool validateMove(int from, int to) {
//
//if (Color.of(_board[from]) != _sideToMove) return false;
return true;
//(StepValidate.validate(this, Move(from, to)));
}
bool regret() {
// TODO
final lastMove = _recorder.removeLast();
if (lastMove == null) return false;
_grid[lastMove.from] = _grid[lastMove.to];
_grid[lastMove.to] = lastMove.captured;
_grid[lastMove.to] = lastMove.removed;
board[lastMove.from] = board[lastMove.to];
board[lastMove.to] = lastMove.captured;
board[lastMove.to] = lastMove.removed;
changeSideToMove();
final counterMarks = MillRecorder.fromCounterMarks(lastMove.counterMarks);
final counterMarks = GameRecorder.fromCounterMarks(lastMove.counterMarks);
_recorder.halfMove = counterMarks.halfMove;
_recorder.fullMove = counterMarks.fullMove;
if (lastMove.captured != Piece.noPiece) {
if (lastMove.removed != Piece.noPiece) {
//
// NativeEngine
final tempPosition = Position.clone(this);
final moves = _recorder.reverseMovesToPrevCapture();
final moves = _recorder.reverseMovesToPrevRemove();
moves.forEach((move) {
//
tempPosition._grid[move.from] = tempPosition._grid[move.to];
tempPosition._grid[move.to] = move.captured;
tempPosition._grid[move.to] = move.removed;
tempPosition._sideToMove = Color.opponent(tempPosition._sideToMove);
});
_recorder.lastCapturedPosition = tempPosition.fen();
_recorder.lastPositionWithRemove = tempPosition.fen();
}
result = GameResult.pending;
@ -1617,20 +1504,20 @@ class Position {
return true;
}
String movesSinceLastCaptured() {
String movesSinceLastRemove() {
//
var steps = '', posAfterLastCaptured = 0;
var moves = '', posAfterLastRemove = 0;
for (var i = _recorder.stepsCount - 1; i >= 0; i--) {
if (_recorder.stepAt(i).captured != Piece.noPiece) break;
posAfterLastCaptured = i;
for (var i = _recorder.movesCount - 1; i >= 0; i--) {
if (_recorder.stepAt(i).removed != Piece.noPiece) break;
posAfterLastRemove = i;
}
for (var i = posAfterLastCaptured; i < _recorder.stepsCount; i++) {
steps += ' ${_recorder.stepAt(i).move}';
for (var i = posAfterLastRemove; i < _recorder.movesCount; i++) {
moves += ' ${_recorder.stepAt(i).move}';
}
return steps.length > 0 ? steps.substring(1) : '';
return moves.length > 0 ? moves.substring(1) : '';
}
get manualText => _recorder.buildManualText();
@ -1649,5 +1536,5 @@ class Position {
get lastMove => _recorder.last;
get lastCapturedPosition => _recorder.lastCapturedPosition;
get lastPositionWithRemove => _recorder.lastPositionWithRemove;
}

View File

@ -20,16 +20,16 @@
import 'mill.dart';
import 'position.dart';
class MillRecorder {
class GameRecorder {
//
//
int halfMove, fullMove;
String lastCapturedPosition;
String lastPositionWithRemove;
final _history = <Move>[];
MillRecorder(
{this.halfMove = 0, this.fullMove = 0, this.lastCapturedPosition});
MillRecorder.fromCounterMarks(String marks) {
GameRecorder(
{this.halfMove = 0, this.fullMove = 0, this.lastPositionWithRemove});
GameRecorder.fromCounterMarks(String marks) {
//
var segments = marks.split(' ');
if (segments.length != 2) {
@ -43,9 +43,9 @@ class MillRecorder {
throw 'Error: Invalid Counter Marks: $marks';
}
}
void stepIn(Move move, Position position) {
void moveIn(Move move, Position position) {
//
if (move.captured != Piece.noPiece) {
if (move.removed != Piece.noPiece) {
halfMove = 0;
} else {
halfMove++;
@ -59,8 +59,8 @@ class MillRecorder {
_history.add(move);
if (move.captured != Piece.noPiece) {
lastCapturedPosition = position.fen();
if (move.removed != Piece.noPiece) {
lastPositionWithRemove = position.fen();
}
}
@ -71,12 +71,12 @@ class MillRecorder {
get last => _history.isEmpty ? null : _history.last;
List<Move> reverseMovesToPrevCapture() {
List<Move> reverseMovesToPrevRemove() {
//
List<Move> moves = [];
for (var i = _history.length - 1; i >= 0; i--) {
if (_history[i].captured != Piece.noPiece) break;
if (_history[i].removed != Piece.noPiece) break;
moves.add(_history[i]);
}
@ -101,7 +101,7 @@ class MillRecorder {
Move stepAt(int index) => _history[index];
get stepsCount => _history.length;
get movesCount => _history.length;
@override
String toString() {

View File

@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:sanmill/common/misc.dart';
import 'package:sanmill/common/algorithm.dart';
enum MoveType { place, move, remove }
@ -91,13 +91,20 @@ const lineDirectionNumber = 3;
enum File { A, B, C }
const fileNumber = 3;
const fileExNumber = fileNumber + 2;
enum Rank { rank_1, rank_2, rank_3, rank_4, rank_5, rank_6, rank_7, rank_8 }
const rankNumber = 8;
bool isOk(int sq) {
return sq == 0 || (sq >= 8 && sq <= 31); // TODO: SQ_NONE?
bool ret = (sq == 0 || (sq >= 8 && sq <= 31));
if (ret == false) {
print("$sq is not OK");
}
return ret; // TODO: SQ_NONE?
}
int fileOf(int sq) {
@ -118,20 +125,6 @@ int toSq(int move) {
return (move & 0x00FF);
}
MoveType typeOf(int move) {
if (move < 0) {
return MoveType.remove;
} else if (move & 0x1f00 > 0) {
return MoveType.move;
}
return MoveType.place; // m & 0x00ff
}
int makeMove(int fromSq, int toSq) {
return (fromSq << 8) + toSq;
}
int reverseMove(int move) {
return makeMove(toSq(move), fromSq(move));
int makeMove(int from, int to) {
return (from << 8) + to;
}

View File

@ -18,25 +18,23 @@
*/
import 'package:flutter/material.dart';
import 'package:sanmill/style/colors.dart';
import 'package:sanmill/widgets/board.dart';
import '../board/painter_base.dart';
import '../common/properties.dart';
import 'board_widget.dart';
import 'painter_base.dart';
class BoardPainter extends PainterBase {
//
class BoardPainter extends PiecesBasePainter {
BoardPainter({@required double width}) : super(width: width);
@override
void paint(Canvas canvas, Size size) {
//
doPaint(
canvas,
thePaint,
gridWidth,
squareWidth,
offsetX: BoardWidget.padding + squareWidth / 2,
offsetY: BoardWidget.padding + BoardWidget.digitsHeight + squareWidth / 2,
offsetX: Board.padding + squareWidth / 2,
offsetY: Board.padding + Board.digitsHeight + squareWidth / 2,
);
}
@ -53,8 +51,7 @@ class BoardPainter extends PainterBase {
double offsetX,
double offsetY,
}) {
//
paint.color = Properties.boardLineColor;
paint.color = UIColors.boardLineColor;
paint.style = PaintingStyle.stroke;
const double borderLineWidth = 2.0;

View File

@ -18,18 +18,16 @@
*/
import 'package:flutter/material.dart';
import 'package:sanmill/widgets/board.dart';
import 'board_widget.dart';
abstract class PainterBase extends CustomPainter {
//
abstract class PiecesBasePainter extends CustomPainter {
final double width;
final thePaint = Paint();
final gridWidth;
final squareWidth;
PainterBase({@required this.width})
: gridWidth = (width - BoardWidget.padding * 2),
squareWidth = (width - BoardWidget.padding * 2) / 7;
PiecesBasePainter({@required this.width})
: gridWidth = (width - Board.padding * 2),
squareWidth = (width - Board.padding * 2) / 7;
}

View File

@ -18,21 +18,20 @@
*/
import 'package:flutter/material.dart';
import 'package:sanmill/mill/mill.dart';
import 'package:sanmill/mill/position.dart';
import 'package:sanmill/style/colors.dart';
import 'package:sanmill/widgets/board.dart';
import '../board/painter_base.dart';
import '../common/properties.dart';
import '../mill/mill.dart';
import '../mill/position.dart';
import 'board_widget.dart';
import 'painter_base.dart';
class PiecePaintStub {
class PiecePaintPair {
final String piece;
final Offset pos;
PiecePaintStub({this.piece, this.pos});
PiecePaintPair({this.piece, this.pos});
}
class PiecesPainter extends PainterBase {
//
class PiecesPainter extends PiecesBasePainter {
final Position position;
final int focusIndex, blurIndex;
@ -41,8 +40,8 @@ class PiecesPainter extends PainterBase {
PiecesPainter({
@required double width,
@required this.position,
this.focusIndex = Move.invalidValue,
this.blurIndex = Move.invalidValue,
this.focusIndex = Move.invalidMove,
this.blurIndex = Move.invalidMove,
}) : super(width: width) {
//
pieceWidth = squareWidth * 0.9; //
@ -50,7 +49,6 @@ class PiecesPainter extends PainterBase {
@override
void paint(Canvas canvas, Size size) {
//
doPaint(
canvas,
thePaint,
@ -59,8 +57,8 @@ class PiecesPainter extends PainterBase {
squareWidth: squareWidth,
pieceWidth: pieceWidth,
// 线
offsetX: BoardWidget.padding + squareWidth / 2,
offsetY: BoardWidget.padding + BoardWidget.digitsHeight + squareWidth / 2,
offsetX: Board.padding + squareWidth / 2,
offsetY: Board.padding + Board.digitsHeight + squareWidth / 2,
focusIndex: focusIndex,
blurIndex: blurIndex,
);
@ -81,19 +79,18 @@ class PiecesPainter extends PainterBase {
double pieceWidth,
double offsetX,
double offsetY,
int focusIndex = Move.invalidValue,
int blurIndex = Move.invalidValue,
int focusIndex = Move.invalidMove,
int blurIndex = Move.invalidMove,
}) {
//
final left = offsetX;
final top = offsetY;
final shadowPath = Path();
final piecesToDraw = <PiecePaintStub>[];
final piecesToDraw = <PiecePaintPair>[];
//
for (var row = 0; row < 7; row++) {
//
for (var col = 0; col < 7; col++) {
//
final piece = position.pieceOnGrid(row * 7 + col); //
@ -102,7 +99,7 @@ class PiecesPainter extends PainterBase {
var pos = Offset(left + squareWidth * col, top + squareWidth * row);
piecesToDraw.add(PiecePaintStub(piece: piece, pos: pos));
piecesToDraw.add(PiecePaintPair(piece: piece, pos: pos));
shadowPath.addOval(
Rect.fromCenter(center: pos, width: pieceWidth, height: pieceWidth),
@ -130,22 +127,22 @@ class PiecesPainter extends PainterBase {
//
switch (pps.piece) {
case Piece.blackStone:
paint.color = Properties.blackPieceBorderColor;
paint.color = UIColors.blackPieceBorderColor;
canvas.drawCircle(pps.pos, pieceRadius, paint); //
paint.color = Properties.blackPieceColor;
paint.color = UIColors.blackPieceColor;
canvas.drawCircle(pps.pos, pieceInnerRadius, paint);
break;
case Piece.whiteStone:
paint.color = Properties.whitePieceBorderColor;
paint.color = UIColors.whitePieceBorderColor;
canvas.drawCircle(pps.pos, pieceRadius, paint); //
paint.color = Properties.whitePieceColor;
paint.color = UIColors.whitePieceColor;
canvas.drawCircle(pps.pos, pieceInnerRadius, paint);
break;
case Piece.ban:
print("pps.piece is Ban");
paint.color = Properties.banBorderColor;
paint.color = UIColors.banBorderColor;
// TODO
paint.color = Properties.banColor;
paint.color = UIColors.banColor;
canvas.drawLine(new Offset(0, 0),
new Offset(pieceInnerRadius, pieceInnerRadius), paint);
canvas.drawLine(new Offset(pieceInnerRadius, 0),
@ -155,33 +152,15 @@ class PiecesPainter extends PainterBase {
assert(false);
break;
}
/*
final textSpan = TextSpan(text: Piece.Names[pps.piece], style: textStyle);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
)..layout();
final metric = textPainter.computeLineMetrics()[0];
final textSize = textPainter.size;
// Baseline 2/3 线
final textOffset = pps.pos - Offset(textSize.width / 2, metric.baseline - textSize.height / 3);
textPainter.paint(canvas, textOffset);
*/
});
// draw focus and blur position
if (focusIndex != Move.invalidValue) {
if (focusIndex != Move.invalidMove) {
//
final int row = focusIndex ~/ 7, column = focusIndex % 7;
paint.color = Properties.focusPositionColor;
paint.color = UIColors.focusPositionColor;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 2;
@ -192,11 +171,10 @@ class PiecesPainter extends PainterBase {
);
}
if (blurIndex != Move.invalidValue) {
//
if (blurIndex != Move.invalidMove) {
final row = blurIndex ~/ 7, column = blurIndex % 7;
paint.color = Properties.blurPositionColor;
paint.color = UIColors.blurPositionColor;
paint.style = PaintingStyle.fill;
canvas.drawCircle(

View File

@ -29,7 +29,6 @@ class Audios {
//
try {
if (_bgmPlayer == null) {
//
_fixedBgmPlayer = AudioPlayer();
_bgmPlayer =
AudioCache(prefix: 'audios/', fixedPlayer: _fixedBgmPlayer);
@ -43,7 +42,6 @@ class Audios {
}
static playTone(String fileName) async {
//
try {
if (_tonePlayer == null) {
//

View File

@ -32,7 +32,6 @@ class Player extends RankItem {
static get shared => _instance;
static Future<Player> loadProfile() async {
//
if (_instance == null) {
_instance = Player();
await _instance._load();
@ -42,7 +41,6 @@ class Player extends RankItem {
}
_load() async {
//
final profile = await Profile.shared();
_uuid = profile['player-uuid'];
@ -56,14 +54,14 @@ class Player extends RankItem {
name = values['name'] ?? '无名英雄';
winCloudEngine = values['win_cloud_engine'] ?? 0;
winPhoneAi = values['win_phone_ai'] ?? 0;
winAi = values['win_ai'] ?? 0;
}
}
Player() : super.empty();
Future<void> increaseWinPhoneAi() async {
winPhoneAi++;
Future<void> increaseWinAi() async {
winAi++;
await saveAndUpload();
}

View File

@ -23,33 +23,33 @@ import 'dart:io';
class RankItem {
//
String name;
int winCloudEngine, winPhoneAi;
int winCloudEngine, winAi;
RankItem(Map<String, dynamic> values) {
name = values['name'] ?? '无名英雄';
winCloudEngine = values['win_cloud_engine'] ?? 0;
winPhoneAi = values['win_phone_ai'] ?? 0;
winAi = values['win_ai'] ?? 0;
}
RankItem.empty() {
name = '无名英雄';
winCloudEngine = 0;
winPhoneAi = 0;
winAi = 0;
}
RankItem.mock() {
name = '我是英雄';
winCloudEngine = 3;
winPhoneAi = 12;
winAi = 12;
}
Map<String, dynamic> toMap() => {
'name': name,
'win_cloud_engine': winCloudEngine,
'win_phone_ai': winPhoneAi,
'win_ai': winAi,
};
get score => winCloudEngine * 30 + winPhoneAi * 5;
get score => winCloudEngine * 30 + winAi * 5;
}
class Ranks {
@ -113,7 +113,7 @@ class Ranks {
'uuid': uuid,
'name': rank.name,
'win_cloud_engine': '${rank.winCloudEngine}',
'win_phone_ai': '${rank.winPhoneAi}',
'win_ai': '${rank.winAi}',
});
final httpClient = HttpClient();

View File

@ -19,7 +19,7 @@
import 'package:flutter/material.dart';
class Properties {
class UIColors {
//
static const logoColor = Color(0xFF6D000D);

View File

@ -18,14 +18,14 @@
*/
import 'package:flutter/material.dart';
import 'package:sanmill/mill/game.dart';
import 'package:sanmill/painting/board_painter.dart';
import 'package:sanmill/painting/pieces_painter.dart';
import 'package:sanmill/style/colors.dart';
import '../common/properties.dart';
import '../game/battle.dart';
import 'board_painter.dart';
import 'pieces_painter.dart';
import 'words_on_board.dart';
class BoardWidget extends StatelessWidget {
class Board extends StatelessWidget {
//
static const padding = 5.0;
static const digitsHeight = 0.0;
@ -36,8 +36,7 @@ class BoardWidget extends StatelessWidget {
final double height;
final Function(BuildContext, int) onBoardTap;
BoardWidget({@required this.width, @required this.onBoardTap})
: height = width;
Board({@required this.width, @required this.onBoardTap}) : height = width;
@override
Widget build(BuildContext context) {
@ -47,15 +46,15 @@ class BoardWidget extends StatelessWidget {
height: height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(boardBorderRadius),
color: Properties.boardBackgroundColor,
color: UIColors.boardBackgroundColor,
),
child: CustomPaint(
painter: BoardPainter(width: width),
foregroundPainter: PiecesPainter(
width: width,
position: Battle.shared.position,
focusIndex: Battle.shared.focusIndex,
blurIndex: Battle.shared.blurIndex,
position: Game.shared.position,
focusIndex: Game.shared.focusIndex,
blurIndex: Game.shared.blurIndex,
),
child: Container(
margin: EdgeInsets.symmetric(

View File

@ -18,8 +18,7 @@
*/
import 'package:flutter/material.dart';
import '../common/properties.dart';
import 'package:sanmill/style/colors.dart';
class EditPage extends StatefulWidget {
//
@ -58,7 +57,7 @@ class _EditPageState extends State<EditPage> {
//
final inputBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(25),
borderSide: BorderSide(color: Properties.secondaryColor),
borderSide: BorderSide(color: UIColors.secondaryColor),
);
return Scaffold(
@ -72,7 +71,7 @@ class _EditPageState extends State<EditPage> {
)
],
),
backgroundColor: Properties.lightBackgroundColor,
backgroundColor: UIColors.lightBackgroundColor,
body: Container(
margin: EdgeInsets.all(16),
child: Column(
@ -86,7 +85,7 @@ class _EditPageState extends State<EditPage> {
focusedBorder: inputBorder,
),
style: TextStyle(
color: Properties.primaryColor, fontSize: 16, fontFamily: ''),
color: UIColors.primaryColor, fontSize: 16, fontFamily: ''),
onSubmitted: (input) => onSubmit(input),
focusNode: _commentFocus,
),

View File

@ -18,53 +18,49 @@
*/
import 'package:flutter/material.dart';
import 'package:sanmill/engine/analyze.dart';
import 'package:sanmill/engine/engine.dart';
import 'package:sanmill/engine/native_engine.dart';
import 'package:sanmill/main.dart';
import 'package:sanmill/mill/game.dart';
import 'package:sanmill/mill/mill.dart';
import 'package:sanmill/mill/types.dart';
import 'package:sanmill/services/player.dart';
import 'package:sanmill/style/colors.dart';
import 'package:sanmill/style/toast.dart';
import '../board/board_widget.dart';
import '../common/properties.dart';
import '../common/toast.dart';
import '../common/types.dart';
import '../engine/analysis.dart';
import '../engine/engine.dart';
import '../engine/native_engine.dart';
import '../game/battle.dart';
import '../main.dart';
import '../mill/mill.dart';
import '../services/player.dart';
import 'board.dart';
import 'settings_page.dart';
class BattlePage extends StatefulWidget {
class GamePage extends StatefulWidget {
//
static double boardMargin = 10.0, screenPaddingH = 10.0;
final EngineType engineType;
final AiEngine engine;
BattlePage(this.engineType) : engine = NativeEngine();
GamePage(this.engineType) : engine = NativeEngine();
@override
_BattlePageState createState() => _BattlePageState();
_GamePageState createState() => _GamePageState();
}
class _BattlePageState extends State<BattlePage> {
class _GamePageState extends State<GamePage> {
//
String _status = '';
bool _analysising = false;
//static int flag = 0;
bool _searching = false;
@override
void initState() {
//
super.initState();
Battle.shared.init();
Game.shared.init();
widget.engine.startup();
}
changeStatus(String status) => setState(() => _status = status);
onBoardTap(BuildContext context, int index) {
final position = Battle.shared.position;
final position = Game.shared.position;
int sq = indexToSquare[index];
@ -75,13 +71,13 @@ class _BattlePageState extends State<BattlePage> {
}
// AI
if (Battle.shared.isAIsTurn() || Battle.shared.aiIsSearching()) {
if (Game.shared.isAiToMove() || Game.shared.aiIsSearching()) {
return false;
}
//
if (position.phase == Phase.ready) {
Battle.shared.gameStart();
Game.shared.start();
}
//
@ -92,11 +88,11 @@ class _BattlePageState extends State<BattlePage> {
if (position.putPiece(sq)) {
if (position.action == Act.remove) {
//
//playSound(GAME_SOUND_MILL, position.side_to_move());
//Audios.playTone('mill.mp3');
changeStatus('请吃子');
} else {
//
//playSound(GAME_SOUND_DROG, position.side_to_move());
//Audios.playTone('put.mp3');
changeStatus('已落子');
}
result = true;
@ -112,19 +108,16 @@ class _BattlePageState extends State<BattlePage> {
continue select;
select:
case Act.select:
//piece = qgraphicsitem_cast<PieceItem *>(item);
//if (!piece)
//break;
if (position.selectPiece(sq)) {
//
//playSound(GAME_SOUND_SELECT, position.side_to_move());
Battle.shared.select(index);
//Audios.playTone('select.mp3');
Game.shared.select(index);
result = true;
print("selectPiece: [$sq]");
changeStatus('请落子');
} else {
//
//playSound(GAME_SOUND_BANNED, position.side_to_move());
//Audios.playTone('banned.mp3');
print("selectPiece: skip [$sq]");
changeStatus('选择的子不对');
}
@ -133,13 +126,13 @@ class _BattlePageState extends State<BattlePage> {
case Act.remove:
if (position.removePiece(sq)) {
//
//playSound(GAME_SOUND_REMOVE, position.side_to_move());
//Audios.playTone('remove.mp3');
result = true;
print("removePiece: [$sq]");
changeStatus('已吃子');
} else {
//
//playSound(GAME_SOUND_BANNED, position.side_to_move());
//Audios.playTone('banned.mp3');
print("removePiece: skip [$sq]");
changeStatus('不能吃这个子');
}
@ -151,135 +144,46 @@ class _BattlePageState extends State<BattlePage> {
}
if (result) {
Battle.shared.cmdlist.add(position.cmdline);
Game.shared.moveHistory.add(position.cmdline);
//
setState(() {});
//message = QString::fromStdString(getTips());
//emit statusBarChanged(message);
// ListModel
/*
currentRow = manualListModel.rowCount() - 1;
int k = 0;
//
for (const auto & i : *(cmd_list())) {
// list容器没有下标
if (k++ <= currentRow)
continue;
manualListModel.insertRow(++currentRow);
manualListModel.setData(manualListModel.index(currentRow), i.c_str());
}
*/
//
/*
String winner = position.winner;
if (winner != Color.nobody &&
(manualListModel.data(manualListModel.index(currentRow - 1)))
.toString()
.contains("Time over.")) playSound(GAME_SOUND_WIN, winner);
*/
// AI设置
//
if (position.winner == Color.nobody) {
// Color.black is TODO
//resumeAiThreads(position.sideToMove());
engineToGo();
}
}
Battle.shared.sideToMove = position.sideToMove();
Game.shared.sideToMove = position.sideToMove();
setState(() {});
return result;
// TODO:
// Position side
//if (position.side != Color.black) return;
final tapedPiece = position.pieceOnGrid(index);
print("Tap piece $tapedPiece at <$index>");
switch (position.phase) {
case Phase.placing:
engineToGo();
break;
case Phase.moving:
//
if (Battle.shared.focusIndex != Move.invalidValue &&
Color.of(position.pieceOnGrid(Battle.shared.focusIndex)) ==
Color.black) {
//
//
if (Battle.shared.focusIndex == index) return;
//
final focusPiece = position.pieceOnGrid(Battle.shared.focusIndex);
if (Color.isSameColor(focusPiece, tapedPiece)) {
//
Battle.shared.select(index);
//
} else if (Battle.shared.move(Battle.shared.focusIndex, index)) {
//
final result = Battle.shared.scanBattleResult();
switch (result) {
case GameResult.pending:
engineToGo();
break;
case GameResult.win:
gotWin();
break;
case GameResult.lose:
gotLose();
break;
case GameResult.draw:
gotDraw();
break;
}
}
//
} else {
//
if (tapedPiece != Piece.noPiece) Battle.shared.select(index);
}
break;
default:
break;
}
setState(() {});
}
engineToGo() async {
// TODO
while (Battle.shared.position.sideToMove() == Color.white) {
changeStatus('电脑思考中...');
while (Game.shared.position.sideToMove() == Color.white) {
changeStatus('对方思考中...');
final response = await widget.engine.search(Battle.shared.position);
final response = await widget.engine.search(Game.shared.position);
if (response.type == 'move') {
//
Move mv = response.value;
final Move move = new Move(mv.move);
//Battle.shared.move = move;
Battle.shared.command(move.move);
Game.shared.command(move.move);
final winner = Battle.shared.position.winner;
final winner = Game.shared.position.winner;
switch (winner) {
case Color.nobody:
if (Battle.shared.position.phase == Phase.placing) {
if (Game.shared.position.phase == Phase.placing) {
changeStatus('请摆子');
} else if (Battle.shared.position.phase == Phase.moving) {
} else if (Game.shared.position.phase == Phase.moving) {
changeStatus('请走子');
}
break;
@ -296,28 +200,22 @@ class _BattlePageState extends State<BattlePage> {
gotDraw();
break;
}
//
} else {
//
changeStatus('Error: ${response.type}');
}
}
}
newGame() {
//
confirm() {
Navigator.of(context).pop();
Battle.shared.newGame();
//setState(() {});
Game.shared.newGame();
changeStatus('新游戏');
if (Battle.shared.isAIsTurn()) {
if (Game.shared.isAiToMove()) {
print("New Game: AI's turn.");
engineToGo();
}
//setState(() {});
}
cancel() => Navigator.of(context).pop();
@ -326,7 +224,7 @@ class _BattlePageState extends State<BattlePage> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('新局?', style: TextStyle(color: Properties.primaryColor)),
title: Text('新局?', style: TextStyle(color: UIColors.primaryColor)),
content: SingleChildScrollView(child: Text('开始新局?')),
actions: <Widget>[
FlatButton(child: Text('确定'), onPressed: confirm),
@ -337,33 +235,32 @@ class _BattlePageState extends State<BattlePage> {
);
}
analysisPosition() async {
analyzePosition() async {
//
Toast.toast(context, msg: '正在分析局面...', position: ToastPostion.bottom);
setState(() => _analysising = true);
setState(() => _searching = true);
try {} catch (e) {
Toast.toast(context, msg: '错误: $e', position: ToastPostion.bottom);
} finally {
setState(() => _analysising = false);
setState(() => _searching = false);
}
}
showAnalysisItems(
showAnalyzeItems(
BuildContext context, {
String title,
List<AnalysisItem> items,
Function(AnalysisItem item) callback,
List<AnalyzeItem> items,
Function(AnalyzeItem item) callback,
}) {
//
final List<Widget> children = [];
for (var item in items) {
children.add(
ListTile(
title: Text(item.stepName, style: TextStyle(fontSize: 18)),
subtitle: Text('胜率:${item.winrate}%'),
title: Text(item.moveName, style: TextStyle(fontSize: 18)),
subtitle: Text('胜率:${item.winRate}%'),
trailing: Text('分数:${item.score}'),
onTap: () => callback(item),
),
@ -384,7 +281,7 @@ class _BattlePageState extends State<BattlePage> {
void gotWin() {
//
Battle.shared.position.result = GameResult.win;
Game.shared.position.result = GameResult.win;
//Audios.playTone('win.mp3');
showDialog(
@ -392,7 +289,7 @@ class _BattlePageState extends State<BattlePage> {
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('赢了', style: TextStyle(color: Properties.primaryColor)),
title: Text('赢了', style: TextStyle(color: UIColors.primaryColor)),
content: Text('恭喜您取得了伟大的胜利!'),
actions: <Widget>[
FlatButton(child: Text('再来一盘'), onPressed: newGame),
@ -407,12 +304,12 @@ class _BattlePageState extends State<BattlePage> {
if (widget.engineType == EngineType.Cloud)
Player.shared.increaseWinCloudEngine();
else
Player.shared.increaseWinPhoneAi();
Player.shared.increaseWinAi();
}
void gotLose() {
//
Battle.shared.position.result = GameResult.lose;
Game.shared.position.result = GameResult.lose;
//Audios.playTone('lose.mp3');
showDialog(
@ -420,7 +317,7 @@ class _BattlePageState extends State<BattlePage> {
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('输了', style: TextStyle(color: Properties.primaryColor)),
title: Text('输了', style: TextStyle(color: UIColors.primaryColor)),
content: Text('勇士!坚定战斗,虽败犹荣!'),
actions: <Widget>[
FlatButton(child: Text('再来一盘'), onPressed: newGame),
@ -435,14 +332,14 @@ class _BattlePageState extends State<BattlePage> {
void gotDraw() {
//
Battle.shared.position.result = GameResult.draw;
Game.shared.position.result = GameResult.draw;
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('<EFBFBD><EFBFBD><EFBFBD>', style: TextStyle(color: Properties.primaryColor)),
title: Text('', style: TextStyle(color: UIColors.primaryColor)),
content: Text('您用自己的力量捍卫了和平!'),
actions: <Widget>[
FlatButton(child: Text('再来一盘'), onPressed: newGame),
@ -463,17 +360,17 @@ class _BattlePageState extends State<BattlePage> {
if (height / width < 16.0 / 9.0) {
width = height * 9 / 16;
BattlePage.screenPaddingH =
(windowSize.width - width) / 2 - BattlePage.boardMargin;
GamePage.screenPaddingH =
(windowSize.width - width) / 2 - GamePage.boardMargin;
}
}
Widget createPageHeader() {
//
final titleStyle =
TextStyle(fontSize: 28, color: Properties.darkTextPrimaryColor);
TextStyle(fontSize: 28, color: UIColors.darkTextPrimaryColor);
final subTitleStyle =
TextStyle(fontSize: 16, color: Properties.darkTextSecondaryColor);
TextStyle(fontSize: 16, color: UIColors.darkTextSecondaryColor);
return Container(
margin: EdgeInsets.only(top: SanmillApp.StatusBarHeight),
@ -483,7 +380,7 @@ class _BattlePageState extends State<BattlePage> {
children: <Widget>[
IconButton(
icon: Icon(Icons.arrow_back,
color: Properties.darkTextPrimaryColor),
color: UIColors.darkTextPrimaryColor),
onPressed: () => Navigator.of(context).pop(),
),
Expanded(child: SizedBox()),
@ -493,8 +390,8 @@ class _BattlePageState extends State<BattlePage> {
style: titleStyle),
Expanded(child: SizedBox()),
IconButton(
icon: Icon(Icons.settings,
color: Properties.darkTextPrimaryColor),
icon:
Icon(Icons.settings, color: UIColors.darkTextPrimaryColor),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SettingsPage()),
),
@ -506,7 +403,7 @@ class _BattlePageState extends State<BattlePage> {
width: 180,
margin: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Properties.boardBackgroundColor,
color: UIColors.boardBackgroundColor,
borderRadius: BorderRadius.circular(2),
),
),
@ -523,12 +420,11 @@ class _BattlePageState extends State<BattlePage> {
//
return Container(
margin: EdgeInsets.symmetric(
horizontal: BattlePage.screenPaddingH,
vertical: BattlePage.boardMargin,
horizontal: GamePage.screenPaddingH,
vertical: GamePage.boardMargin,
),
child: BoardWidget(
width:
MediaQuery.of(context).size.width - BattlePage.screenPaddingH * 2,
child: Board(
width: MediaQuery.of(context).size.width - GamePage.screenPaddingH * 2,
onBoardTap: onBoardTap,
),
);
@ -536,14 +432,14 @@ class _BattlePageState extends State<BattlePage> {
Widget createOperatorBar() {
//
final buttonStyle = TextStyle(color: Properties.primaryColor, fontSize: 20);
final buttonStyle = TextStyle(color: UIColors.primaryColor, fontSize: 20);
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Properties.boardBackgroundColor,
color: UIColors.boardBackgroundColor,
),
margin: EdgeInsets.symmetric(horizontal: BattlePage.screenPaddingH),
margin: EdgeInsets.symmetric(horizontal: GamePage.screenPaddingH),
padding: EdgeInsets.symmetric(vertical: 2),
child: Row(children: <Widget>[
Expanded(child: SizedBox()),
@ -552,14 +448,14 @@ class _BattlePageState extends State<BattlePage> {
FlatButton(
child: Text('悔棋', style: buttonStyle),
onPressed: () {
Battle.shared.regret(steps: 2);
Game.shared.regret(steps: 2);
setState(() {});
},
),
Expanded(child: SizedBox()),
FlatButton(
child: Text('分析', style: buttonStyle),
onPressed: _analysising ? null : analysisPosition,
onPressed: _searching ? null : analyzePosition,
),
Expanded(child: SizedBox()),
]),
@ -570,12 +466,12 @@ class _BattlePageState extends State<BattlePage> {
//
final size = MediaQuery.of(context).size;
final manualText = Battle.shared.position.manualText;
final manualText = Game.shared.position.manualText;
if (size.height / size.width > 16 / 9) {
return buildManualPanel(manualText);
} else {
return buildExpandableManaulPanel(manualText);
return buildExpandableRecordPanel(manualText);
}
}
@ -583,7 +479,7 @@ class _BattlePageState extends State<BattlePage> {
//
final manualStyle = TextStyle(
fontSize: 18,
color: Properties.darkTextSecondaryColor,
color: UIColors.darkTextSecondaryColor,
height: 1.5,
);
@ -595,20 +491,19 @@ class _BattlePageState extends State<BattlePage> {
);
}
Widget buildExpandableManaulPanel(String text) {
Widget buildExpandableRecordPanel(String text) {
//
final manualStyle = TextStyle(fontSize: 18, height: 1.5);
return Expanded(
child: IconButton(
icon: Icon(Icons.expand_less, color: Properties.darkTextPrimaryColor),
icon: Icon(Icons.expand_less, color: UIColors.darkTextPrimaryColor),
onPressed: () => showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title:
Text('棋谱', style: TextStyle(color: Properties.primaryColor)),
title: Text('棋谱', style: TextStyle(color: UIColors.primaryColor)),
content:
SingleChildScrollView(child: Text(text, style: manualStyle)),
actions: <Widget>[
@ -635,7 +530,7 @@ class _BattlePageState extends State<BattlePage> {
final footer = buildFooter();
return Scaffold(
backgroundColor: Properties.darkBackgroundColor,
backgroundColor: UIColors.darkBackgroundColor,
body: Column(children: <Widget>[header, board, operatorBar, footer]),
);
}

View File

@ -18,11 +18,11 @@
*/
import 'package:flutter/material.dart';
import 'package:sanmill/engine/engine.dart';
import 'package:sanmill/main.dart';
import 'package:sanmill/style/colors.dart';
import '../common/properties.dart';
import '../engine/engine.dart';
import '../main.dart';
import 'battle_page.dart';
import 'game_page.dart';
import 'settings_page.dart';
class MainMenu extends StatefulWidget {
@ -31,13 +31,11 @@ class MainMenu extends StatefulWidget {
}
class _MainMenuState extends State<MainMenu> with TickerProviderStateMixin {
//
AnimationController inController, shadowController;
Animation inAnimation, shadowAnimation;
@override
void initState() {
//
super.initState();
inController = AnimationController(
@ -78,7 +76,6 @@ class _MainMenuState extends State<MainMenu> with TickerProviderStateMixin {
}
navigateTo(Widget page) async {
//
await Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => page));
@ -108,7 +105,7 @@ class _MainMenuState extends State<MainMenu> with TickerProviderStateMixin {
);
final menuItemStyle = TextStyle(
fontSize: 28,
color: Properties.primaryColor,
color: UIColors.primaryColor,
shadows: [menuItemShadow],
);
@ -123,7 +120,7 @@ class _MainMenuState extends State<MainMenu> with TickerProviderStateMixin {
Expanded(child: SizedBox()),
FlatButton(
child: Text('人机对战', style: menuItemStyle),
onPressed: () => navigateTo(BattlePage(EngineType.Native)),
onPressed: () => navigateTo(GamePage(EngineType.Native)),
),
Expanded(child: SizedBox()),
Text('Calcitem',
@ -134,7 +131,7 @@ class _MainMenuState extends State<MainMenu> with TickerProviderStateMixin {
);
return Scaffold(
backgroundColor: Properties.lightBackgroundColor,
backgroundColor: UIColors.lightBackgroundColor,
body: Stack(
children: <Widget>[
menuItems,
@ -142,7 +139,7 @@ class _MainMenuState extends State<MainMenu> with TickerProviderStateMixin {
top: SanmillApp.StatusBarHeight,
left: 10,
child: IconButton(
icon: Icon(Icons.settings, color: Properties.primaryColor),
icon: Icon(Icons.settings, color: UIColors.primaryColor),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SettingsPage()),
),

View File

@ -20,12 +20,12 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:package_info/package_info.dart';
import 'package:sanmill/common/config.dart';
import 'package:sanmill/services/audios.dart';
import 'package:sanmill/services/player.dart';
import 'package:sanmill/style/colors.dart';
import 'package:sanmill/style/toast.dart';
import '../common/config.dart';
import '../common/properties.dart';
import '../common/toast.dart';
import '../services/audios.dart';
import '../services/player.dart';
import 'edit_page.dart';
class SettingsPage extends StatefulWidget {
@ -34,8 +34,7 @@ class SettingsPage extends StatefulWidget {
}
class _SettingsPageState extends State<SettingsPage> {
//
String _version = 'Ver 1.00';
String _version = "";
@override
void initState() {
@ -53,12 +52,12 @@ class _SettingsPageState extends State<SettingsPage> {
changeDifficult() {
//
callback(int stepTime) async {
callback(int thinkingTime) async {
//
Navigator.of(context).pop();
setState(() {
Config.stepTime = stepTime;
Config.thinkingTime = thinkingTime;
});
Config.save();
@ -71,25 +70,25 @@ class _SettingsPageState extends State<SettingsPage> {
children: <Widget>[
SizedBox(height: 10),
RadioListTile(
activeColor: Properties.primaryColor,
activeColor: UIColors.primaryColor,
title: Text('初级'),
groupValue: Config.stepTime,
groupValue: Config.thinkingTime,
value: 5000,
onChanged: callback,
),
Divider(),
RadioListTile(
activeColor: Properties.primaryColor,
activeColor: UIColors.primaryColor,
title: Text('中级'),
groupValue: Config.stepTime,
groupValue: Config.thinkingTime,
value: 15000,
onChanged: callback,
),
Divider(),
RadioListTile(
activeColor: Properties.primaryColor,
activeColor: UIColors.primaryColor,
title: Text('高级'),
groupValue: Config.stepTime,
groupValue: Config.thinkingTime,
value: 30000,
onChanged: callback,
),
@ -150,8 +149,7 @@ class _SettingsPageState extends State<SettingsPage> {
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title:
Text('关于「直棋 」', style: TextStyle(color: Properties.primaryColor)),
title: Text('关于「直棋 」', style: TextStyle(color: UIColors.primaryColor)),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@ -187,11 +185,11 @@ class _SettingsPageState extends State<SettingsPage> {
Widget build(BuildContext context) {
//
final TextStyle headerStyle =
TextStyle(color: Properties.secondaryColor, fontSize: 20.0);
final TextStyle itemStyle = TextStyle(color: Properties.primaryColor);
TextStyle(color: UIColors.secondaryColor, fontSize: 20.0);
final TextStyle itemStyle = TextStyle(color: UIColors.primaryColor);
return Scaffold(
backgroundColor: Properties.lightBackgroundColor,
backgroundColor: UIColors.lightBackgroundColor,
appBar: AppBar(title: Text('设置')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
@ -202,7 +200,7 @@ class _SettingsPageState extends State<SettingsPage> {
Text("人机难度", style: headerStyle),
const SizedBox(height: 10.0),
Card(
color: Properties.boardBackgroundColor,
color: UIColors.boardBackgroundColor,
elevation: 0.5,
margin: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 0),
child: Column(
@ -211,11 +209,13 @@ class _SettingsPageState extends State<SettingsPage> {
title: Text("游戏难度", style: itemStyle),
trailing:
Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
Text(Config.stepTime <= 5000
Text(Config.thinkingTime <= 5000
? '初级'
: Config.stepTime <= 15000 ? '中级' : '高级'),
: Config.thinkingTime <= 15000
? '中级'
: '高级'),
Icon(Icons.keyboard_arrow_right,
color: Properties.secondaryColor),
color: UIColors.secondaryColor),
]),
onTap: changeDifficult,
),
@ -225,19 +225,19 @@ class _SettingsPageState extends State<SettingsPage> {
const SizedBox(height: 16),
Text("声音", style: headerStyle),
Card(
color: Properties.boardBackgroundColor,
color: UIColors.boardBackgroundColor,
margin: const EdgeInsets.symmetric(vertical: 10),
child: Column(
children: <Widget>[
SwitchListTile(
activeColor: Properties.primaryColor,
activeColor: UIColors.primaryColor,
value: Config.bgmEnabled,
title: Text("背景音乐", style: itemStyle),
onChanged: switchMusic,
),
_buildDivider(),
SwitchListTile(
activeColor: Properties.primaryColor,
activeColor: UIColors.primaryColor,
value: Config.toneEnabled,
title: Text("提示音效", style: itemStyle),
onChanged: switchTone,
@ -248,7 +248,7 @@ class _SettingsPageState extends State<SettingsPage> {
const SizedBox(height: 16),
Text("排行榜", style: headerStyle),
Card(
color: Properties.boardBackgroundColor,
color: UIColors.boardBackgroundColor,
margin: const EdgeInsets.symmetric(vertical: 10),
child: Column(
children: <Widget>[
@ -258,7 +258,7 @@ class _SettingsPageState extends State<SettingsPage> {
Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
Text(Player.shared.name),
Icon(Icons.keyboard_arrow_right,
color: Properties.secondaryColor),
color: UIColors.secondaryColor),
]),
onTap: changeName,
),
@ -268,7 +268,7 @@ class _SettingsPageState extends State<SettingsPage> {
const SizedBox(height: 16),
Text("关于", style: headerStyle),
Card(
color: Properties.boardBackgroundColor,
color: UIColors.boardBackgroundColor,
margin: const EdgeInsets.symmetric(vertical: 10),
child: Column(
children: <Widget>[
@ -278,7 +278,7 @@ class _SettingsPageState extends State<SettingsPage> {
Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
Text(_version ?? ''),
Icon(Icons.keyboard_arrow_right,
color: Properties.secondaryColor),
color: UIColors.secondaryColor),
]),
onTap: showAbout,
),
@ -297,7 +297,7 @@ class _SettingsPageState extends State<SettingsPage> {
margin: const EdgeInsets.symmetric(horizontal: 16),
width: double.infinity,
height: 1.0,
color: Properties.lightLineColor,
color: UIColors.lightLineColor,
);
}
}

View File

@ -18,8 +18,7 @@
*/
import 'package:flutter/material.dart';
import '../common/properties.dart';
import 'package:sanmill/style/colors.dart';
class WordsOnBoard extends StatelessWidget {
//
@ -45,7 +44,7 @@ class WordsOnBoard extends StatelessWidget {
Row(children: rChildren),
],
),
style: TextStyle(color: Properties.boardTipsColor),
style: TextStyle(color: UIColors.boardTipsColor),
);
}
}