diff --git a/src/ui/flutter/lib/common/types.dart b/src/ui/flutter/lib/common/types.dart index 192bb0f7..eb79fa56 100644 --- a/src/ui/flutter/lib/common/types.dart +++ b/src/ui/flutter/lib/common/types.dart @@ -23,7 +23,7 @@ enum MoveType { place, move, remove } enum Phase { none, ready, placing, moving, gameOver } -enum Action { none, select, place, remove } +enum Act { none, select, place, remove } enum GameOverReason { noReason, diff --git a/src/ui/flutter/lib/mill/position.dart b/src/ui/flutter/lib/mill/position.dart index b4e52d7e..e410f0e0 100644 --- a/src/ui/flutter/lib/mill/position.dart +++ b/src/ui/flutter/lib/mill/position.dart @@ -17,9 +17,12 @@ along with this program. If not, see . */ +import 'package:flutter/cupertino.dart'; + import '../common/types.dart'; import '../mill/mill.dart'; import '../mill/recorder.dart'; +import '../mill/rule.dart'; class StateInfo { /* @@ -63,7 +66,7 @@ class Position { GameOverReason gameOverReason = GameOverReason.noReason; Phase phase = Phase.none; - Action action = Action.none; + Act action = Act.none; int scoreBlack = 0; int scoreWhite = 0; @@ -72,7 +75,9 @@ class Position { int currentSquare; int nPlayed = 0; - //Move move; + String cmdline; + + //int _move; Position.init() { for (var i = 0; i < _grid.length; i++) { @@ -145,6 +150,112 @@ class Position { bool empty(int sq) => pieceOn(sq) == Piece.noPiece; + void updateScore() {} + + void setSideToMove(String color) { + _sideToMove = color; + } + + String movedPiece(int move) { + return pieceOn(fromSq(move)); + } + + bool selectPieceFR(int file, int rank) { + return selectPieceSQ(makeSquare(file, rank)); + } + + void putPiece(var pt, int index) { + _grid[index] = pt; + _board[indexToSquare[index]] = pt; + } + + 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 (selectPieceSQ(fromSq)) { + return putPieceSQ(toSq); + } + + return false; + } + + 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() { @@ -195,13 +306,13 @@ class Position { // Action switch (action) { - case Action.place: + case Act.place: ss += "p"; break; - case Action.select: + case Act.select: ss += "s"; break; - case Action.remove: + case Act.remove: ss += "r"; break; default: @@ -235,11 +346,79 @@ class Position { return ss; } - void putPiece(var pt, int index) { - _grid[index] = pt; - _board[indexToSquare[index]] = pt; + /// Position::legal() tests whether a pseudo-legal move is legal + + bool legal(int move) { + assert(isOk(move)); + + String us = _sideToMove; + int fromSQ = fromSq(move); + int toSQ = toSq(move); + + if (fromSQ == toSQ) { + return false; // TODO: Same with is_ok(m) + } + + if (phase == Phase.moving && typeOf(move) != MoveType.remove) { + if (movedPiece(move) != us) { + return false; + } + } + + // TODO: Add more + + 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); + } + + /* + /// Position::do_move() makes a move, and saves all information necessary + /// to a StateInfo object. The move is assumed to be legal. Pseudo-legal + /// moves should be filtered out before this function is called. + + void doMove(int move) { + bool ret = false; + + MoveType mt = typeOf(move); + + switch (mt) { + case MoveType.remove: + // Reset rule 50 counter + rule50 = 0; + ret = removePiece(toSq(move)); + break; + case MoveType.move: + ret = movePiece(fromSq(move), toSq(move)); + break; + case MoveType.place: + ret = putPieceSQ(toSq(move)); + break; + default: + break; + } + + if (!ret) { + return; + } + + // Increment ply counters. In particular, rule50 will be reset to zero later on + // in case of a capture. + ++gamePly; + ++rule50; + ++pliesFromNull; + + _move = move; + } + */ + String doMove(int from, int to) { // if (!validateMove(from, to)) return null; @@ -351,4 +530,237 @@ class Position { get lastMove => _recorder.last; get lastCapturedPosition => _recorder.lastCapturedPosition; + +/////////////////////////////////////////////////////////////////////////////// + + int piecesOnBoardCount() { + pieceCountOnBoardBlack = pieceCountOnBoardWhite = 0; + + for (int f = 1; f < 3 + 2; f++) { + for (int r = 0; r < 8; r++) { + int s = f * 8 + r; + if (_board[s] == Piece.blackStone) { + pieceCountOnBoardBlack++; + } else if (_board[s] == Piece.whiteStone) { + pieceCountOnBoardBlack++; + } + } + } + + if (pieceCountOnBoardBlack > rule.nTotalPiecesEachSide || + pieceCountOnBoardWhite > rule.nTotalPiecesEachSide) { + return -1; + } + + return pieceCountOnBoardBlack + pieceCountOnBoardWhite; + } + + int piecesInHandCount() { + pieceCountInHandBlack = rule.nTotalPiecesEachSide - pieceCountOnBoardBlack; + pieceCountInHandWhite = rule.nTotalPiecesEachSide - pieceCountOnBoardWhite; + + return pieceCountOnBoardBlack + pieceCountOnBoardWhite; + } + + int setPosition(Rule newRule) { + rule = newRule; + + gamePly = 0; + rule50 = 0; + + phase = Phase.ready; + setSideToMove(Color.black); + action = Act.place; + + for (int i = 0; i < _grid.length; i++) _grid[i] = Piece.noPiece; + + for (int i = 0; i < _board.length; i++) _board[i] = Piece.noPiece; + + if (piecesOnBoardCount() == -1) { + return -1; + } + + piecesInHandCount(); + pieceCountNeedRemove = 0; + + winner = Color.unknown; + + currentSquare = 0; + return -1; + } + + bool reset() { + gamePly = 0; + rule50 = 0; + + phase = Phase.ready; + setSideToMove(Color.black); + action = Act.place; + + winner = Color.unknown; + 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; + + pieceCountOnBoardBlack = pieceCountOnBoardWhite = 0; + pieceCountInHandBlack = pieceCountInHandWhite = rule.nTotalPiecesEachSide; + pieceCountNeedRemove = 0; + + currentSquare = 0; + int i = 0; // TODO: rule + + cmdline = "r" + + (i + 1).toString() + + " " + + "s" + + rule.maxStepsLedToDraw.toString() + + " t" + + 0.toString(); + + return false; + } + + bool start() { + gameOverReason = GameOverReason.noReason; + + switch (phase) { + case Phase.placing: + case Phase.moving: + return false; + case Phase.gameOver: + reset(); + continue ready; + ready: + case Phase.ready: + phase = Phase.placing; + return true; + default: + return false; + } + } + + /* + bool putPiece(int sq) + { + String piece = Piece.noPiece; + String us = _sideToMove; + + if (phase == Phase.gameOver || + action != Act.place || + sq < 0 || sq >= 31 || _board[sq] != Piece.noPiece) { + return false; + } + + if (phase == Phase.ready) { + start(); + } + + if (phase == Phase.placing) { + piece = _sideToMove; + if (_sideToMove == Color.black) { + pieceCountInHandBlack--; + pieceCountOnBoardBlack++; + } + else if (_sideToMove == Color.white) { + pieceCountInHandWhite--; + pieceCountOnBoardWhite++; + } + + _board[sq]= piece; + _grid[squareToIndex[sq]] = piece; + + cmdline = "(" + fileOf(sq).toString() + "," + rankOf(sq).toString() + ")"; + + currentSquare = sq; + + int n = addMills(currentSquare); + + if (n == 0) { + assert(pieceCountInHandBlack >= 0 && pieceCountInHandWhite >= 0); + + if (pieceCountInHandBlack == 0 && pieceCountInHandWhite == 0) { + if (checkGameOverCondition()) { + return true; + } + + phase = Phase.moving; + action = Act.select; + + if (rule.hasBannedLocations) { + removeBanStones(); + } + + if (!rule.isDefenderMoveFirst) { + changeSideToMove(); + } + + if (checkGameOverCondition()) { + return true; + } + } else { + changeSideToMove(); + } + } else { + pieceCountNeedRemove = rule.allowRemoveMultiPiecesWhenCloseMultiMill ? n : 1; + action = Act.remove; + } + + } else if (phase == Phase.moving) { + + if (checkGameOverCondition()) { + return true; + } + + // if illegal + if (pieceCountOnBoard[sideToMove] > rule->nPiecesAtLeast || + !rule->allowFlyWhenRemainThreePieces) { + int md; + + for (md = 0; md < MD_NB; md++) { + if (s == MoveList::moveTable[currentSquare][md]) + break; + } + + // not in moveTable + if (md == MD_NB) { + return false; + } + } + + if (updateCmdlist) { + sprintf(cmdline, "(%1u,%1u)->(%1u,%1u)", + file_of(currentSquare), rank_of(currentSquare), + file_of(s), rank_of(s)); + st.rule50++; + } + + board[s] = board[currentSquare]; + + board[currentSquare] = NO_PIECE; + + currentSquare = s; + int n = add_mills(currentSquare); + + // midgame + if (n == 0) { + action = ACTION_SELECT; + change_side_to_move(); + + if (check_gameover_condition()) { + return true; + } + } else { + pieceCountNeedRemove = rule->allowRemoveMultiPiecesWhenCloseMultiMill ? n : 1; + update_key_misc(); + action = ACTION_REMOVE; + } + } else { + assert(0); + } + + return true; + } + */ }