Initial commit

master
Dany Thach 2025-02-09 11:47:39 +01:00
commit 637cb053c4
15 changed files with 2324 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# project-specific
builds/

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Dany Thach
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

60
README.md Normal file
View File

@ -0,0 +1,60 @@
# Omnichess
Omnichess is a UCI-compatible chess engine developed in Dart. This engine was created as a fun project and is not intended for competitive use. It implements the basic functionality required by the UCI protocol so that it can be interfaced with various chess GUIs.
## Features
- UCI Protocol Support: Communicate with chess GUIs using standard UCI commands.
- Written in Dart: Leverages Dart's simplicity and ease of use.
- Fun Implementation: Designed for learning and experimenting rather than serious chess analysis.
## Getting Started
### Prerequisites
- Dart SDK (version 3.6.1 or compatible)
Ensure Dart is properly installed by running:
```bash
dart --version
```
### Running the Engine
To run the engine, execute the following command in your terminal from the root of the project:
```bash
dart run
```
### Compiling the Engine
If you want better performance, you can compile the engine to get an executable that can be executed by your GUI.
To compile the engine, execute the following command from the root of the project:
```bash
dart compile exe -o <output-file>
```
Replace `<output-file>` with the name you want to give to the executable.
### UCI Setup
After starting the engine, configure your chess GUI to use the UCI protocol. You should set the engine command to either the above `dart run` command or the executable file if you choose to compile it. The engine will then communicate with the GUI for move generation and game state updates.
## Limitations
- This engine is a fun project and should not be considered a serious chess engine.
- The focus is on UCI compatibility, and while it works with standard command inputs and outputs, its analysis may not be on par with competitive-level engines.
## Contributing
Feel free to fork the repository and experiment. Contributions and suggestions are welcome, but please keep in mind that this is primarily a hobby project.
## License
This project is provided "as is" without any warranty. Use it for personal or educational purposes, but do not expect competitive level performance.
Happy coding and enjoy playing with Omnichess!

30
analysis_options.yaml Normal file
View File

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

6
bin/omnichess.dart Normal file
View File

@ -0,0 +1,6 @@
import 'package:omnichess/omnichess.dart';
void main(List<String> arguments) {
final Omnichess engine = Omnichess();
engine.loop();
}

View File

@ -0,0 +1,76 @@
import 'dart:collection';
import 'package:omnichess/classes/position.dart';
import 'package:omnichess/constants/piece.dart';
import 'package:omnichess/functions/legal_moves.dart';
class BestMoveSearcher {
bool _isRunning = false;
bool _isStopped = false;
Future<int> getPositionBaseValue(Position position) async {
if (await LegalMoves.isCheckmate(position)) {
return position.isWhiteTurn
? -(1 << 63) // min int (white is checkmated)
: ((1 << 63) - 1); // max int (black is checkmated)
}
int value = 0;
for (int i = 0; i < 64; i++) {
value += switch (position.board[i]) {
Piece.whiteQueen => 900,
Piece.whiteRook => 500,
Piece.whiteBishop => 300,
Piece.whiteKnight => 300,
Piece.whitePawn => 100,
Piece.blackQueen => -900,
Piece.blackRook => -500,
Piece.blackBishop => -300,
Piece.blackKnight => -300,
Piece.blackPawn => -100,
_ => 0,
};
}
return value;
}
Future<String?> search(Position position) async {
_isRunning = true;
(int, String, Position)? bestMove;
Queue<(int, String, Position)> evaluatedMoves = Queue();
await for (final (String, Position) move in LegalMoves.getLegalMoves(position)) {
final (int, String, Position) evaluatedMove = (await getPositionBaseValue(move.$2), move.$1, move.$2);
if (
null == bestMove ||
(bestMove.$1 < evaluatedMove.$1 && position.isWhiteTurn) ||
(bestMove.$1 > evaluatedMove.$1 && !position.isWhiteTurn)
) {
bestMove = evaluatedMove;
}
evaluatedMoves.add(evaluatedMove);
await Future.delayed(Duration.zero);
if (_isStopped) {
break;
}
// TODO evaluate checkmate
}
if (evaluatedMoves.isEmpty) {
_isRunning = false;
_isStopped = false;
return null;
}
// TODO search
// IDEA implement a BFS with pruning on the ones that give suboptimal values
_isRunning = false;
_isStopped = false;
return bestMove?.$2;
}
Future<void> stop() async {
if (!_isRunning) {
return;
}
_isStopped = true;
}
}

366
lib/classes/position.dart Normal file
View File

@ -0,0 +1,366 @@
import 'dart:collection';
import 'dart:io';
import 'package:omnichess/constants/piece.dart';
class Position {
static List<int> get startingBoard => <int>[
Piece.blackRook, Piece.blackKnight, Piece.blackBishop, Piece.blackQueen, Piece.blackKing, Piece.blackBishop, Piece.blackKnight, Piece.blackRook,
Piece.blackPawn, Piece.blackPawn, Piece.blackPawn, Piece.blackPawn, Piece.blackPawn, Piece.blackPawn, Piece.blackPawn, Piece.blackPawn,
Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare,
Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare,
Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare,
Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare, Piece.emptySquare,
Piece.whitePawn, Piece.whitePawn, Piece.whitePawn, Piece.whitePawn, Piece.whitePawn, Piece.whitePawn, Piece.whitePawn, Piece.whitePawn,
Piece.whiteRook, Piece.whiteKnight, Piece.whiteBishop, Piece.whiteQueen, Piece.whiteKing, Piece.whiteBishop, Piece.whiteKnight, Piece.whiteRook,
];
List<int> board;
bool isWhiteTurn;
bool canWhiteCastleKingside;
bool canWhiteCastleQueenside;
bool canBlackCastleKingside;
bool canBlackCastleQueenside;
int? enPassantTargetSquare;
int halfmoveClock;
int fullmoveNumber;
String? get enPassantTargetSquareAlgebraic => null == enPassantTargetSquare ? null : String.fromCharCodes([
97 + (enPassantTargetSquare! % 8),
56 - (enPassantTargetSquare! ~/ 8)
]);
String get fen {
int i = 0;
final StringBuffer stringBuffer = StringBuffer();
while (i < 64) {
if (board[i] == 0) {
int emptyCount = 1;
i++;
while (i % 8 != 0 && board[i] == 0) {
emptyCount++;
i++;
}
stringBuffer.write(emptyCount.toString());
} else {
stringBuffer.write(
Piece.fenCharacterByPiece[board[i]],
);
i++;
}
if (i < 64 && i % 8 == 0) {
stringBuffer.write("/");
}
}
return "$stringBuffer"
" "
"${isWhiteTurn ? "w" : "b"}"
" "
"${canWhiteCastleKingside ? "K" : ""}"
"${canWhiteCastleQueenside ? "Q" : ""}"
"${canBlackCastleKingside ? "k" : ""}"
"${canBlackCastleQueenside ? "q" : ""}"
"${!(canWhiteCastleKingside || canWhiteCastleQueenside || canBlackCastleKingside || canBlackCastleQueenside) ? "-" : ""}"
" "
"${enPassantTargetSquareAlgebraic ?? "-"}"
" "
"$halfmoveClock"
" "
"$fullmoveNumber";
}
Position({
required this.board,
required this.isWhiteTurn,
required this.canWhiteCastleKingside,
required this.canWhiteCastleQueenside,
required this.canBlackCastleKingside,
required this.canBlackCastleQueenside,
required this.enPassantTargetSquare,
required this.halfmoveClock,
required this.fullmoveNumber,
});
factory Position.from(Position position) => Position(
board : List<int>.from(position.board, growable: false),
isWhiteTurn : position.isWhiteTurn,
canWhiteCastleKingside : position.canWhiteCastleKingside,
canWhiteCastleQueenside: position.canWhiteCastleQueenside,
canBlackCastleKingside : position.canBlackCastleKingside,
canBlackCastleQueenside: position.canBlackCastleQueenside,
enPassantTargetSquare : position.enPassantTargetSquare,
halfmoveClock : position.halfmoveClock,
fullmoveNumber : position.fullmoveNumber,
);
factory Position.fromStartingPosition() => Position(
board : startingBoard,
isWhiteTurn : true,
canWhiteCastleKingside : true,
canWhiteCastleQueenside: true,
canBlackCastleKingside : true,
canBlackCastleQueenside: true,
enPassantTargetSquare : null,
halfmoveClock : 0,
fullmoveNumber : 1,
);
factory Position.fromFen(
String fenPieces,
String fenTurnPlayer,
String fenCastlingRights,
String fenEnPassantTargetSquare,
String fenHalfmoveClock,
String fenFullmoveNumber,
) {
const String legalPieces = "pnbrqk";
int boardIndex = 0;
final List<int> newBoard = List<int>.filled(64, Piece.emptySquare);
for (int fenPiecesIndex = 0; fenPiecesIndex < fenPieces.length; fenPiecesIndex++) {
final String fenPiece = fenPieces[fenPiecesIndex];
final int? fenPieceNumber = int.tryParse(fenPiece);
if (fenPiece == "/") {
// Row separator, no action needed.
} else if (null != fenPieceNumber) {
boardIndex += fenPieceNumber;
} else if (legalPieces.contains(fenPiece.toLowerCase())) {
newBoard[boardIndex] = Piece.pieceByFenCharacter[fenPiece]!;
boardIndex++;
} else {
throw FormatException("Invalid character $fenPiece", fenPieces, fenPiecesIndex);
}
}
if (boardIndex != 64) {
throw FormatException("Invalid board size: Expected 64, Found $boardIndex", fenPieces);
}
final List<int> board = newBoard;
final bool isWhiteTurn = fenTurnPlayer == "w";
final bool canWhiteCastleKingside = fenCastlingRights.contains("K");
final bool canWhiteCastleQueenside = fenCastlingRights.contains("Q");
final bool canBlackCastleKingside = fenCastlingRights.contains("k");
final bool canBlackCastleQueenside = fenCastlingRights.contains("q");
final int? enPassantTargetSquare = fenEnPassantTargetSquare == "-"
? null
: squareStringToIndex(fenEnPassantTargetSquare);
final int halfmoveClock = int.parse(fenHalfmoveClock);
final int fullmoveNumber = int.parse(fenFullmoveNumber);
return Position(
board : board,
isWhiteTurn : isWhiteTurn,
canWhiteCastleKingside : canWhiteCastleKingside,
canWhiteCastleQueenside: canWhiteCastleQueenside,
canBlackCastleKingside : canBlackCastleKingside,
canBlackCastleQueenside: canBlackCastleQueenside,
enPassantTargetSquare : enPassantTargetSquare,
halfmoveClock : halfmoveClock,
fullmoveNumber : fullmoveNumber,
);
}
factory Position.fromPositionCommand(String command) {
final Queue<String> commandComponents = Queue<String>.from(command.split(" "));
final String type = commandComponents.removeFirst();
Position position = switch (type) {
"fen" => (() {
final String fenPieces = commandComponents.removeFirst();
final String fenTurnPlayer = commandComponents.removeFirst();
final String fenCastlingRights = commandComponents.removeFirst();
final String fenEnPassantTargetSquare = commandComponents.removeFirst();
final String fenHalfmoveClock = commandComponents.removeFirst();
final String fenFullmoveNumber = commandComponents.removeFirst();
return Position.fromFen(
fenPieces,
fenTurnPlayer,
fenCastlingRights,
fenEnPassantTargetSquare,
fenHalfmoveClock,
fenFullmoveNumber,
);
})(),
"startpos" => Position.fromStartingPosition(),
_ => throw FormatException("Invalid position type $type"),
};
if (commandComponents.isEmpty) {
return position;
}
commandComponents.removeFirst(); // "moves"
while (commandComponents.isNotEmpty) {
final String move = commandComponents.removeFirst();
position.playMove(move);
}
return position;
}
void playMoveIndices(int startSquare, int endSquare, [String? promotingPiece]) => isWhiteTurn
? _playMoveWhite(startSquare, endSquare, promotingPiece)
: _playMoveBlack(startSquare, endSquare, promotingPiece);
void playMove(String move) {
final (int startSquare, int endSquare, String? promotingPiece) = moveStringToIndices(move);
playMoveIndices(startSquare, endSquare, promotingPiece);
}
void _playMoveWhite(int startSquare, int endSquare, String? promotingPiece) {
board[endSquare] = board[startSquare];
board[startSquare] = Piece.emptySquare;
isWhiteTurn = false;
if (0 == endSquare) { // sth moved to a8
canBlackCastleQueenside = false;
} else if (7 == endSquare) { // sth moved to h8
canBlackCastleKingside = false;
}
if (Piece.whitePawn == board[endSquare]) {
halfmoveClock = 0;
if (enPassantTargetSquare == endSquare) {
board[endSquare + 8] = Piece.emptySquare;
}
enPassantTargetSquare = (startSquare - endSquare == 16) ? startSquare - 8 : null;
if (endSquare < 8) {
board[endSquare] = Piece.pieceByFenCharacter[promotingPiece]!;
}
return;
}
halfmoveClock++;
enPassantTargetSquare = null;
if (Piece.whiteKing == board[endSquare]) {
canWhiteCastleQueenside = false;
canWhiteCastleKingside = false;
if (startSquare - endSquare == 2) { // queenside castle
board[56] = Piece.emptySquare;
board[59] = Piece.whiteRook;
return;
}
if (endSquare - startSquare == 2) { // kingside castle
board[63] = Piece.emptySquare;
board[61] = Piece.whiteRook;
return;
}
return;
}
if (56 == startSquare) { // sth from a1 moved
canWhiteCastleQueenside = false;
} else if (63 == startSquare) { // sth from h1 moved
canWhiteCastleKingside = false;
}
return;
}
void _playMoveBlack(int startSquare, int endSquare, String? promotingPiece) {
board[endSquare] = board[startSquare];
board[startSquare] = Piece.emptySquare;
isWhiteTurn = true;
fullmoveNumber = fullmoveNumber + 1;
if (56 == endSquare) { // sth moved to a8
canWhiteCastleQueenside = false;
} else if (63 == endSquare) { // sth moved to h8
canWhiteCastleKingside = false;
}
if (Piece.blackPawn == board[endSquare]) {
halfmoveClock = 0;
if (enPassantTargetSquare == endSquare) {
board[endSquare - 8] = Piece.emptySquare;
}
enPassantTargetSquare = (endSquare - startSquare == 16) ? startSquare + 8 : null;
if (endSquare > 55) {
board[endSquare] = Piece.pieceByFenCharacter[promotingPiece]!;
}
return;
}
halfmoveClock++;
enPassantTargetSquare = null;
if (Piece.blackKing == board[endSquare]) {
canBlackCastleQueenside = false;
canBlackCastleKingside = false;
if (startSquare - endSquare == 2) { // queenside castle
board[0] = Piece.emptySquare;
board[3] = Piece.blackRook;
return;
}
if (endSquare - startSquare == 2) { // kingside castle
board[7] = Piece.emptySquare;
board[5] = Piece.blackRook;
return;
}
}
if (0 == startSquare) { // sth from a8 moved
canBlackCastleQueenside = false;
} else if (7 == startSquare) { // sth from h8 moved
canBlackCastleKingside = false;
}
}
void setStartingPosition() {
board = startingBoard;
isWhiteTurn = true;
canWhiteCastleKingside = true;
canWhiteCastleQueenside = true;
canBlackCastleKingside = true;
canBlackCastleQueenside = true;
enPassantTargetSquare = null;
halfmoveClock = 0;
fullmoveNumber = 1;
}
bool setFenPosition(
String fenPieces,
String fenTurnPlayer,
String fenCastlingRights,
String fenEnPassantTargetSquare,
String fenHalfmoveClock,
String fenFullmoveNumber,
) {
try {
const String legalPieces = "pnbrqk";
int boardIndex = 0;
final List<int> newBoard = List<int>.filled(64, Piece.emptySquare);
for (int fenPiecesIndex = 0; fenPiecesIndex < fenPieces.length; fenPiecesIndex++) {
final String fenPiece = fenPieces[fenPiecesIndex];
final int? fenPieceNumber = int.tryParse(fenPiece);
if (fenPiece == "/") {
// Row separator, no action needed.
} else if (null != fenPieceNumber) {
boardIndex += fenPieceNumber;
} else if (legalPieces.contains(fenPiece.toLowerCase())) {
newBoard[boardIndex] = Piece.pieceByFenCharacter[fenPiece]!;
boardIndex++;
} else {
// If character is not recognized return false.
return false;
}
}
if (boardIndex != 64) {
return false;
}
board = newBoard;
isWhiteTurn = fenTurnPlayer == "w";
canWhiteCastleKingside = fenCastlingRights.contains("K");
canWhiteCastleQueenside = fenCastlingRights.contains("Q");
canBlackCastleKingside = fenCastlingRights.contains("k");
canBlackCastleQueenside = fenCastlingRights.contains("q");
enPassantTargetSquare = fenEnPassantTargetSquare == "-"
? null
: squareStringToIndex(fenEnPassantTargetSquare);
halfmoveClock = int.parse(fenHalfmoveClock);
fullmoveNumber = int.parse(fenFullmoveNumber);
return true;
} catch (e) {
stderr.writeln("error \"$e\"");
return false;
}
}
static String squareIndexToString(int index) => String.fromCharCodes([
97 + (index % 8),
56 - (index ~/ 8)
]);
static int squareStringToIndex(String square) => (56 - square.codeUnitAt(1)) * 8 + square.codeUnitAt(0) - 97;
static (int, int, String?) moveStringToIndices(String move) => (
(56 - move.codeUnitAt(1)) * 8 + move.codeUnitAt(0) - 97,
(56 - move.codeUnitAt(3)) * 8 + move.codeUnitAt(2) - 97,
move.length > 4 ? move.substring(4).toLowerCase() : null,
);
}

55
lib/constants/piece.dart Normal file
View File

@ -0,0 +1,55 @@
import "package:meta/meta.dart";
@immutable
class Piece {
// Private constructor to prevent instantiation.
Piece._();
static const emptySquare = 0;
static const whiteKing = 1;
static const whiteQueen = 2;
static const whiteRook = 3;
static const whiteBishop = 4;
static const whiteKnight = 5;
static const whitePawn = 6;
static const blackKing = 7;
static const blackQueen = 8;
static const blackRook = 9;
static const blackBishop = 10;
static const blackKnight = 11;
static const blackPawn = 12;
static const Map<String, int> pieceByFenCharacter = {
"k": blackKing,
"q": blackQueen,
"r": blackRook,
"b": blackBishop,
"n": blackKnight,
"p": blackPawn,
"K": whiteKing,
"Q": whiteQueen,
"R": whiteRook,
"B": whiteBishop,
"N": whiteKnight,
"P": whitePawn,
};
static const Map<int, String> fenCharacterByPiece = <int, String>{
Piece.emptySquare: " ",
Piece.blackKing : "k",
Piece.blackQueen : "q",
Piece.blackRook : "r",
Piece.blackBishop: "b",
Piece.blackKnight: "n",
Piece.blackPawn : "p",
Piece.whiteKing : "K",
Piece.whiteQueen : "Q",
Piece.whiteRook : "R",
Piece.whiteBishop: "B",
Piece.whiteKnight: "N",
Piece.whitePawn : "P",
};
static bool isWhite(int piece) => piece >= whiteKing && piece <= whitePawn;
static bool isBlack(int piece) => piece >= blackKing && piece <= blackPawn;
}

View File

@ -0,0 +1,3 @@
class KingNotFoundException implements Exception {
}

View File

@ -0,0 +1,571 @@
part of "../legal_moves.dart";
class _LegalMovesBlack {
_LegalMovesBlack._();
static Stream<(String, Position)> _getLegalMoves(Position position) => _getValidOrCheckedMoves(position).where(((String, Position) move) => !_isBlackChecked(move.$2));
static Stream<(String, Position)> _getValidOrCheckedMoves(Position position) async* {
for (int i = 0; i < 64; i++) {
switch (position.board[i]) {
case Piece.emptySquare:
case Piece.whitePawn:
case Piece.whiteKnight:
case Piece.whiteBishop:
case Piece.whiteRook:
case Piece.whiteQueen:
case Piece.whiteKing:
break;
case Piece.blackPawn:
yield* _getBlackPawnMoves(position, i);
break;
case Piece.blackKnight:
yield* _getBlackKnightMoves(position, i);
break;
case Piece.blackBishop:
yield* _getBlackBishopMoves(position, i);
break;
case Piece.blackRook:
yield* _getBlackRookMoves(position, i);
break;
case Piece.blackQueen:
yield* _getBlackBishopMoves(position, i);
yield* _getBlackRookMoves(position, i);
break;
case Piece.blackKing:
yield* _getBlackKingMoves(position, i);
break;
}
}
}
static Stream<(String, Position)> _getBlackPawnMoves(Position position, int i) async* {
int j = i+8;
if (Piece.emptySquare == position.board[j]) { // pawn forward
if (i > 47) {
final String squareFrom = Position.squareIndexToString(i);
final String squareTo = Position.squareIndexToString(j);
yield ("$squareFrom${squareTo}q", Position.from(position)..playMoveIndices(i, j, "q"));
yield ("$squareFrom${squareTo}r", Position.from(position)..playMoveIndices(i, j, "r"));
yield ("$squareFrom${squareTo}b", Position.from(position)..playMoveIndices(i, j, "b"));
yield ("$squareFrom${squareTo}k", Position.from(position)..playMoveIndices(i, j, "k"));
} else {
final String squareFrom = Position.squareIndexToString(i);
yield ("$squareFrom${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
j = i+16;
if (i < 16 && Piece.emptySquare == position.board[j]) { // pawn more forward
yield ("$squareFrom${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
}
j = i+7;
if (i % 8 > 0 && (j == position.enPassantTargetSquare || Piece.isWhite(position.board[j]))) { // pawn takes left
if (i > 47) {
final String squareFrom = Position.squareIndexToString(i);
final String squareTo = Position.squareIndexToString(j);
yield ("$squareFrom${squareTo}q", Position.from(position)..playMoveIndices(i, j, "q"));
yield ("$squareFrom${squareTo}r", Position.from(position)..playMoveIndices(i, j, "r"));
yield ("$squareFrom${squareTo}b", Position.from(position)..playMoveIndices(i, j, "b"));
yield ("$squareFrom${squareTo}k", Position.from(position)..playMoveIndices(i, j, "k"));
} else {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j = i+9;
if (i % 8 < 7 && (j == position.enPassantTargetSquare || Piece.isWhite(position.board[j]))) { // pawn takes right
if (i > 47) {
final String squareFrom = Position.squareIndexToString(i);
final String squareTo = Position.squareIndexToString(j);
yield ("$squareFrom${squareTo}q", Position.from(position)..playMoveIndices(i, j, "q"));
yield ("$squareFrom${squareTo}r", Position.from(position)..playMoveIndices(i, j, "r"));
yield ("$squareFrom${squareTo}b", Position.from(position)..playMoveIndices(i, j, "b"));
yield ("$squareFrom${squareTo}k", Position.from(position)..playMoveIndices(i, j, "k"));
} else {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
}
static Stream<(String, Position)> _getBlackKnightMoves(Position position, int i) async* {
final bool canGoUp2 = i > 15;
final bool canGoUp1 = i > 7;
final bool canGoDown1 = i < 56;
final bool canGoDown2 = i < 48;
final int column = i % 8;
final bool canGoLeft2 = column > 1;
final bool canGoLeft1 = column > 0;
final bool canGoRight1 = column < 7;
final bool canGoRight2 = column < 6;
if (canGoUp1) {
if (canGoLeft2 && !Piece.isBlack(position.board[i-10])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i-10)}", Position.from(position)..playMoveIndices(i, i-10));
}
if (canGoRight2 && !Piece.isBlack(position.board[i-6])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i-6)}", Position.from(position)..playMoveIndices(i, i-6));
}
if (canGoUp2) {
if (canGoLeft1 && !Piece.isBlack(position.board[i-17])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i-17)}", Position.from(position)..playMoveIndices(i, i-17));
}
if (canGoRight1 && !Piece.isBlack(position.board[i-15])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i-15)}", Position.from(position)..playMoveIndices(i, i-15));
}
}
}
if (canGoDown1) {
if (canGoLeft2 && !Piece.isBlack(position.board[i+6])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i+6)}", Position.from(position)..playMoveIndices(i, i+6));
}
if (canGoRight2 && !Piece.isBlack(position.board[i+10])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i+10)}", Position.from(position)..playMoveIndices(i, i+10));
}
if (canGoDown2) {
if (canGoLeft1 && !Piece.isBlack(position.board[i+15])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i+15)}", Position.from(position)..playMoveIndices(i, i+15));
}
if (canGoRight1 && !Piece.isBlack(position.board[i+17])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i+17)}", Position.from(position)..playMoveIndices(i, i+17));
}
}
}
}
static Stream<(String, Position)> _getBlackBishopMoves(Position position, int i) async* {
final int column = i % 8;
// up left
bool hasEncounteredPiece = false;
int k = column-1;
int j = i-9;
while (!hasEncounteredPiece && k >= 0 && j >= 0) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
k--;
j-=9;
}
// up right
hasEncounteredPiece = false;
k = column+1;
j = i-7;
while (!hasEncounteredPiece && k < 8 && j >= 0) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
k++;
j-=7;
}
// down left
hasEncounteredPiece = false;
k = column-1;
j = i+7;
while (!hasEncounteredPiece && k >= 0 && j < 64) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
k--;
j+=7;
}
// down right
hasEncounteredPiece = false;
k = column+1;
j = i+9;
while (!hasEncounteredPiece && k < 8 && j < 64) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
k++;
j+=9;
}
}
static Stream<(String, Position)> _getBlackRookMoves(Position position, int i) async* {
final int column = i % 8;
// up
bool hasEncounteredPiece = false;
int j = i-8;
while (!hasEncounteredPiece && j > 0) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j-=8;
}
// down
hasEncounteredPiece = false;
j = i+8;
while (!hasEncounteredPiece && j < 64) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j+=8;
}
// left
hasEncounteredPiece = false;
int k = column-1;
j = i-1;
while (!hasEncounteredPiece && k >= 0) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j-=1;
k-=1;
}
// right
hasEncounteredPiece = false;
k = column+1;
j = i+1;
while (!hasEncounteredPiece && k < 8) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j+=1;
k+=1;
}
}
static Stream<(String, Position)> _getBlackKingMoves(Position position, int i) async* {
int j = i-8;
if (j >= 0 && !Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i+8;
if (j < 64 && !Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
if (i % 8 > 0) {
j = i-1;
if (!Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i-9;
if (j >= 0 && !Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i+7;
if (j < 64 && !Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
if (i % 8 < 7) {
j = i+1;
if (!Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i-7;
if (j >= 0 && !Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i+9;
if (j < 64 && !Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
if(!_isWhiteAttacking(position, i)) {
j = i-2;
if (
position.canBlackCastleQueenside &&
Piece.emptySquare == position.board[i-1] &&
Piece.emptySquare == position.board[i-2] &&
!_isWhiteAttacking(position, i-1)
) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i+2;
if (
position.canBlackCastleKingside &&
Piece.emptySquare == position.board[i+1] &&
Piece.emptySquare == position.board[i+2] &&
!_isWhiteAttacking(position, i+1)
) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
}
static int _getBlackKingSquare(Position position) {
for (int i = 0; i < 64; i++) {
if (position.board[i] == Piece.blackKing) {
return i;
}
}
throw KingNotFoundException();
}
static bool _isBlackChecked(Position position) => _isWhiteAttacking(position, _getBlackKingSquare(position));
static bool _isWhiteAttacking(Position position, int square) {
if (_isWhitePawnAttacking(position, square)) {
return true;
}
if (_isWhiteKnightAttacking(position, square)) {
return true;
}
if (_isWhiteBishopOrQueenAttacking(position, square)) {
return true;
}
if (_isWhiteRookOrQueenAttacking(position, square)) {
return true;
}
if (_isWhiteKingAttacking(position, square)) {
return true;
}
return false;
}
static bool _isWhitePawnAttacking(Position position, int square) {
if (square < 8) {
return false;
}
final int column = square % 8;
if (column > 0 && Piece.whitePawn == position.board[square + 7]) {
return true;
}
if (column < 7 && Piece.whitePawn == position.board[square + 9]) {
return true;
}
return false;
}
static bool _isWhiteKnightAttacking(Position position, int square) {
final bool canGoUp2 = square > 15;
final bool canGoUp1 = square > 7;
final bool canGoDown1 = square < 56;
final bool canGoDown2 = square < 48;
final int column = square % 8;
final bool canGoLeft2 = column > 1;
final bool canGoLeft1 = column > 0;
final bool canGoRight1 = column < 7;
final bool canGoRight2 = column < 6;
if (canGoUp1) {
if (canGoLeft2 && Piece.whiteKnight == position.board[square-10]) {
return true;
}
if (canGoRight2 && Piece.whiteKnight == position.board[square-6]) {
return true;
}
if (canGoUp2) {
if (canGoLeft1 && Piece.whiteKnight == position.board[square-17]) {
return true;
}
if (canGoRight1 && Piece.whiteKnight == position.board[square-15]) {
return true;
}
}
}
if (canGoDown1) {
if (canGoLeft2 && Piece.whiteKnight == position.board[square+6]) {
return true;
}
if (canGoRight2 && Piece.whiteKnight == position.board[square+10]) {
return true;
}
if (canGoDown2) {
if (canGoLeft1 && Piece.whiteKnight == position.board[square+15]) {
return true;
}
if (canGoRight1 && Piece.whiteKnight == position.board[square+17]) {
return true;
}
}
}
return false;
}
static bool _isWhiteBishopOrQueenAttacking(Position position, int square) {
final int column = square % 8;
// up left
bool hasEncounteredPiece = false;
int k = column-1;
int j = square-9;
while (!hasEncounteredPiece && k >= 0 && j >= 0) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.whiteBishop || Piece.whiteQueen) {
return true;
}
}
k--;
j-=9;
}
// up right
hasEncounteredPiece = false;
k = column+1;
j = square-7;
while (!hasEncounteredPiece && k < 8 && j >= 0) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.whiteBishop || Piece.whiteQueen) {
return true;
}
}
k++;
j-=7;
}
// down left
hasEncounteredPiece = false;
k = column-1;
j = square+7;
while (!hasEncounteredPiece && k >= 0 && j < 64) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.whiteBishop || Piece.whiteQueen) {
return true;
}
}
k--;
j+=7;
}
// down right
hasEncounteredPiece = false;
k = column+1;
j = square+9;
while (!hasEncounteredPiece && k < 8 && j < 64) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.whiteBishop || Piece.whiteQueen) {
return true;
}
}
k++;
j+=9;
}
return false;
}
static bool _isWhiteRookOrQueenAttacking(Position position, int square) {
final int column = square % 8;
// up
bool hasEncounteredPiece = false;
int j = square-8;
while (!hasEncounteredPiece && j > 0) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.whiteRook || Piece.whiteQueen) {
return true;
}
}
j-=8;
}
// down
hasEncounteredPiece = false;
j = square + 8;
while (!hasEncounteredPiece && j < 64) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.whiteRook || Piece.whiteQueen) {
return true;
}
}
j+=8;
}
// left
hasEncounteredPiece = false;
j = square - 1;
int k = column - 1;
while (!hasEncounteredPiece && k > 0) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.whiteRook || Piece.whiteQueen) {
return true;
}
}
j--;
k--;
}
// right
hasEncounteredPiece = false;
j = square + 1;
k = column + 1;
while (!hasEncounteredPiece && k < 8) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.whiteRook || Piece.whiteQueen) {
return true;
}
}
j++;
k++;
}
return false;
}
static bool _isWhiteKingAttacking(Position position, int square) {
if (square > 7 && Piece.whiteKing == position.board[square - 8]) {
return true;
}
if (square < 56 && Piece.whiteKing == position.board[square + 8]) {
return true;
}
if (square % 8 > 0) {
if (Piece.whiteKing == position.board[square - 1]) {
return true;
}
if (square > 7 && Piece.whiteKing == position.board[square - 9]) {
return true;
}
if (square < 56 && Piece.whiteKing == position.board[square + 7]) {
return true;
}
}
if (square % 8 < 7) {
if (Piece.whiteKing == position.board[square + 1]) {
return true;
}
if (square > 7 && Piece.whiteKing == position.board[square - 7]) {
return true;
}
if (square < 56 && Piece.whiteKing == position.board[square + 9]) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,571 @@
part of "../legal_moves.dart";
class _LegalMovesWhite {
_LegalMovesWhite._();
static Stream<(String, Position)> _getLegalMoves(Position position) => _getValidOrCheckedMoves(position).where(((String, Position) move) => !_isWhiteChecked(move.$2));
static Stream<(String, Position)> _getValidOrCheckedMoves(Position position) async* {
for (int i = 0; i < 64; i++) {
switch (position.board[i]) {
case Piece.emptySquare:
case Piece.blackPawn:
case Piece.blackKnight:
case Piece.blackBishop:
case Piece.blackRook:
case Piece.blackQueen:
case Piece.blackKing:
break;
case Piece.whitePawn:
yield* _getWhitePawnMoves(position, i);
break;
case Piece.whiteKnight:
yield* _getWhiteKnightMoves(position, i);
break;
case Piece.whiteBishop:
yield* _getWhiteBishopMoves(position, i);
break;
case Piece.whiteRook:
yield* _getWhiteRookMoves(position, i);
break;
case Piece.whiteQueen:
yield* _getWhiteBishopMoves(position, i);
yield* _getWhiteRookMoves(position, i);
break;
case Piece.whiteKing:
yield* _getWhiteKingMoves(position, i);
break;
}
}
}
static Stream<(String, Position)> _getWhitePawnMoves(Position position, int i) async* {
int j = i-8;
if (Piece.emptySquare == position.board[j]) { // pawn forward
if (i < 16) {
final String squareFrom = Position.squareIndexToString(i);
final String squareTo = Position.squareIndexToString(j);
yield ("$squareFrom${squareTo}q", Position.from(position)..playMoveIndices(i, j, "q"));
yield ("$squareFrom${squareTo}r", Position.from(position)..playMoveIndices(i, j, "r"));
yield ("$squareFrom${squareTo}b", Position.from(position)..playMoveIndices(i, j, "b"));
yield ("$squareFrom${squareTo}k", Position.from(position)..playMoveIndices(i, j, "k"));
} else {
final String squareFrom = Position.squareIndexToString(i);
yield ("$squareFrom${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
j = i-16;
if (i > 47 && Piece.emptySquare == position.board[j]) { // pawn more forward
yield ("$squareFrom${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
}
j = i-9;
if (i % 8 > 0 && (j == position.enPassantTargetSquare || Piece.isBlack(position.board[j]))) { // pawn takes left
if (i < 16) {
final String squareFrom = Position.squareIndexToString(i);
final String squareTo = Position.squareIndexToString(j);
yield ("$squareFrom${squareTo}q", Position.from(position)..playMoveIndices(i, j, "q"));
yield ("$squareFrom${squareTo}r", Position.from(position)..playMoveIndices(i, j, "r"));
yield ("$squareFrom${squareTo}b", Position.from(position)..playMoveIndices(i, j, "b"));
yield ("$squareFrom${squareTo}k", Position.from(position)..playMoveIndices(i, j, "k"));
} else {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j = i-7;
if (i % 8 < 7 && (j == position.enPassantTargetSquare || Piece.isBlack(position.board[j]))) { // pawn takes right
if (i < 16) {
final String squareFrom = Position.squareIndexToString(i);
final String squareTo = Position.squareIndexToString(j);
yield ("$squareFrom${squareTo}q", Position.from(position)..playMoveIndices(i, j, "q"));
yield ("$squareFrom${squareTo}r", Position.from(position)..playMoveIndices(i, j, "r"));
yield ("$squareFrom${squareTo}b", Position.from(position)..playMoveIndices(i, j, "b"));
yield ("$squareFrom${squareTo}k", Position.from(position)..playMoveIndices(i, j, "k"));
} else {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
}
static Stream<(String, Position)> _getWhiteKnightMoves(Position position, int i) async* {
final bool canGoUp2 = i > 15;
final bool canGoUp1 = i > 7;
final bool canGoDown1 = i < 56;
final bool canGoDown2 = i < 48;
final int column = i % 8;
final bool canGoLeft2 = column > 1;
final bool canGoLeft1 = column > 0;
final bool canGoRight1 = column < 7;
final bool canGoRight2 = column < 6;
if (canGoUp1) {
if (canGoLeft2 && !Piece.isWhite(position.board[i-10])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i-10)}", Position.from(position)..playMoveIndices(i, i-10));
}
if (canGoRight2 && !Piece.isWhite(position.board[i-6])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i-6)}", Position.from(position)..playMoveIndices(i, i-6));
}
if (canGoUp2) {
if (canGoLeft1 && !Piece.isWhite(position.board[i-17])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i-17)}", Position.from(position)..playMoveIndices(i, i-17));
}
if (canGoRight1 && !Piece.isWhite(position.board[i-15])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i-15)}", Position.from(position)..playMoveIndices(i, i-15));
}
}
}
if (canGoDown1) {
if (canGoLeft2 && !Piece.isWhite(position.board[i+6])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i+6)}", Position.from(position)..playMoveIndices(i, i+6));
}
if (canGoRight2 && !Piece.isWhite(position.board[i+10])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i+10)}", Position.from(position)..playMoveIndices(i, i+10));
}
if (canGoDown2) {
if (canGoLeft1 && !Piece.isWhite(position.board[i+15])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i+15)}", Position.from(position)..playMoveIndices(i, i+15));
}
if (canGoRight1 && !Piece.isWhite(position.board[i+17])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(i+17)}", Position.from(position)..playMoveIndices(i, i+17));
}
}
}
}
static Stream<(String, Position)> _getWhiteBishopMoves(Position position, int i) async* {
final int column = i % 8;
// up left
bool hasEncounteredPiece = false;
int k = column-1;
int j = i-9;
while (!hasEncounteredPiece && k >= 0 && j >= 0) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
k--;
j-=9;
}
// up right
hasEncounteredPiece = false;
k = column+1;
j = i-7;
while (!hasEncounteredPiece && k < 8 && j >= 0) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
k++;
j-=7;
}
// down left
hasEncounteredPiece = false;
k = column-1;
j = i+7;
while (!hasEncounteredPiece && k >= 0 && j < 64) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
k--;
j+=7;
}
// down right
hasEncounteredPiece = false;
k = column+1;
j = i+9;
while (!hasEncounteredPiece && k < 8 && j < 64) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
k++;
j+=9;
}
}
static Stream<(String, Position)> _getWhiteRookMoves(Position position, int i) async* {
final int column = i % 8;
// up
bool hasEncounteredPiece = false;
int j = i-8;
while (!hasEncounteredPiece && j > 0) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j-=8;
}
// down
hasEncounteredPiece = false;
j = i+8;
while (!hasEncounteredPiece && j < 64) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j+=8;
}
// left
hasEncounteredPiece = false;
int k = column-1;
j = i-1;
while (!hasEncounteredPiece && k >= 0) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j-=1;
k-=1;
}
// right
hasEncounteredPiece = false;
k = column+1;
j = i+1;
while (!hasEncounteredPiece && k < 8) {
if (Piece.emptySquare == position.board[j]) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
} else {
hasEncounteredPiece = true;
if (Piece.isBlack(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
j+=1;
k+=1;
}
}
static Stream<(String, Position)> _getWhiteKingMoves(Position position, int i) async* {
int j = i-8;
if (j >= 0 && !Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i+8;
if (j < 64 && !Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
if (i % 8 > 0) {
j = i-1;
if (!Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i-9;
if (j >= 0 && !Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i+7;
if (j < 64 && !Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
if (i % 8 < 7) {
j = i+1;
if (!Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i-7;
if (j >= 0 && !Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i+9;
if (j < 64 && !Piece.isWhite(position.board[j])) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
if(!_isBlackAttacking(position, i)) {
j = i-2;
if (
position.canWhiteCastleQueenside &&
Piece.emptySquare == position.board[i-1] &&
Piece.emptySquare == position.board[i-2] &&
!_isBlackAttacking(position, i-1)
) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
j = i+2;
if (
position.canWhiteCastleKingside &&
Piece.emptySquare == position.board[i+1] &&
Piece.emptySquare == position.board[i+2] &&
!_isBlackAttacking(position, i+1)
) {
yield ("${Position.squareIndexToString(i)}${Position.squareIndexToString(j)}", Position.from(position)..playMoveIndices(i, j));
}
}
}
static int _getWhiteKingSquare(Position position) {
for (int i = 0; i < 64; i++) {
if (position.board[i] == Piece.whiteKing) {
return i;
}
}
throw KingNotFoundException();
}
static bool _isWhiteChecked(Position position) => _isBlackAttacking(position, _getWhiteKingSquare(position));
static bool _isBlackAttacking(Position position, int square) {
if (_isBlackPawnAttacking(position, square)) {
return true;
}
if (_isBlackKnightAttacking(position, square)) {
return true;
}
if (_isBlackBishopOrQueenAttacking(position, square)) {
return true;
}
if (_isBlackRookOrQueenAttacking(position, square)) {
return true;
}
if (_isBlackKingAttacking(position, square)) {
return true;
}
return false;
}
static bool _isBlackPawnAttacking(Position position, int square) {
if (square < 8) {
return false;
}
final int column = square % 8;
if (column > 0 && Piece.blackPawn == position.board[square - 9]) {
return true;
}
if (column < 7 && Piece.blackPawn == position.board[square - 7]) {
return true;
}
return false;
}
static bool _isBlackKnightAttacking(Position position, int square) {
final bool canGoUp2 = square > 15;
final bool canGoUp1 = square > 7;
final bool canGoDown1 = square < 56;
final bool canGoDown2 = square < 48;
final int column = square % 8;
final bool canGoLeft2 = column > 1;
final bool canGoLeft1 = column > 0;
final bool canGoRight1 = column < 7;
final bool canGoRight2 = column < 6;
if (canGoUp1) {
if (canGoLeft2 && Piece.blackKnight == position.board[square-10]) {
return true;
}
if (canGoRight2 && Piece.blackKnight == position.board[square-6]) {
return true;
}
if (canGoUp2) {
if (canGoLeft1 && Piece.blackKnight == position.board[square-17]) {
return true;
}
if (canGoRight1 && Piece.blackKnight == position.board[square-15]) {
return true;
}
}
}
if (canGoDown1) {
if (canGoLeft2 && Piece.blackKnight == position.board[square+6]) {
return true;
}
if (canGoRight2 && Piece.blackKnight == position.board[square+10]) {
return true;
}
if (canGoDown2) {
if (canGoLeft1 && Piece.blackKnight == position.board[square+15]) {
return true;
}
if (canGoRight1 && Piece.blackKnight == position.board[square+17]) {
return true;
}
}
}
return false;
}
static bool _isBlackBishopOrQueenAttacking(Position position, int square) {
final int column = square % 8;
// up left
bool hasEncounteredPiece = false;
int k = column-1;
int j = square-9;
while (!hasEncounteredPiece && k >= 0 && j >= 0) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.blackBishop || Piece.blackQueen) {
return true;
}
}
k--;
j-=9;
}
// up right
hasEncounteredPiece = false;
k = column+1;
j = square-7;
while (!hasEncounteredPiece && k < 8 && j >= 0) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.blackBishop || Piece.blackQueen) {
return true;
}
}
k++;
j-=7;
}
// down left
hasEncounteredPiece = false;
k = column-1;
j = square+7;
while (!hasEncounteredPiece && k >= 0 && j < 64) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.blackBishop || Piece.blackQueen) {
return true;
}
}
k--;
j+=7;
}
// down right
hasEncounteredPiece = false;
k = column+1;
j = square+9;
while (!hasEncounteredPiece && k < 8 && j < 64) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.blackBishop || Piece.blackQueen) {
return true;
}
}
k++;
j+=9;
}
return false;
}
static bool _isBlackRookOrQueenAttacking(Position position, int square) {
final int column = square % 8;
// up
bool hasEncounteredPiece = false;
int j = square-8;
while (!hasEncounteredPiece && j > 0) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.blackRook || Piece.blackQueen) {
return true;
}
}
j-=8;
}
// down
hasEncounteredPiece = false;
j = square + 8;
while (!hasEncounteredPiece && j < 64) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.blackRook || Piece.blackQueen) {
return true;
}
}
j+=8;
}
// left
hasEncounteredPiece = false;
j = square - 1;
int k = column - 1;
while (!hasEncounteredPiece && k > 0) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.blackRook || Piece.blackQueen) {
return true;
}
}
j--;
k--;
}
// right
hasEncounteredPiece = false;
j = square + 1;
k = column + 1;
while (!hasEncounteredPiece && k < 8) {
final int piece = position.board[j];
if (Piece.emptySquare != piece) {
hasEncounteredPiece = true;
if (piece case Piece.blackRook || Piece.blackQueen) {
return true;
}
}
j++;
k++;
}
return false;
}
static bool _isBlackKingAttacking(Position position, int square) {
if (square > 7 && Piece.blackKing == position.board[square - 8]) {
return true;
}
if (square < 56 && Piece.blackKing == position.board[square + 8]) {
return true;
}
if (square % 8 > 0) {
if (Piece.blackKing == position.board[square - 1]) {
return true;
}
if (square > 7 && Piece.blackKing == position.board[square - 9]) {
return true;
}
if (square < 56 && Piece.blackKing == position.board[square + 7]) {
return true;
}
}
if (square % 8 < 7) {
if (Piece.blackKing == position.board[square + 1]) {
return true;
}
if (square > 7 && Piece.blackKing == position.board[square - 7]) {
return true;
}
if (square < 56 && Piece.blackKing == position.board[square + 9]) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,19 @@
import 'package:omnichess/classes/position.dart';
import 'package:omnichess/constants/piece.dart';
import 'package:omnichess/exceptions/king_not_found.dart';
part "_partials/legal_moves_black.dart";
part "_partials/legal_moves_white.dart";
class LegalMoves {
LegalMoves._();
static Stream<(String, Position)> getLegalMoves(Position position) => position.isWhiteTurn
? _LegalMovesWhite._getLegalMoves(position)
: _LegalMovesBlack._getLegalMoves(position);
static Future<bool> isCheckmate(Position position) async => position.isWhiteTurn
? (_LegalMovesWhite._isWhiteChecked(position) && await getLegalMoves(position).isEmpty)
: (_LegalMovesBlack._isBlackChecked(position) && await getLegalMoves(position).isEmpty);
}

123
lib/omnichess.dart Normal file
View File

@ -0,0 +1,123 @@
import "dart:async";
import "dart:collection";
import "dart:convert";
import "dart:io";
import "package:omnichess/classes/best_move_searcher.dart";
import "package:omnichess/classes/position.dart";
import "package:omnichess/constants/piece.dart";
class Omnichess {
BestMoveSearcher bestMoveSearcher = BestMoveSearcher();
String positionCommand = "startpos";
String? bestMove;
bool debug = false;
Omnichess();
void _positionCommand(Queue<String> inputComponents) {
positionCommand = inputComponents.join(" ");
}
Future<void> _goCommand(Queue<String> inputComponents) async {
final Position position = Position.fromPositionCommand(positionCommand);
final String? bestMove = await bestMoveSearcher.search(position);
print("bestmove ${bestMove ?? "(none)"}");
}
Future<void> _stopCommand() async {
await bestMoveSearcher.stop();
}
Future<bool> elaborate(String line) async {
final Queue<String> inputComponents = Queue.from(line.split(" "));
final String command = inputComponents.removeFirst();
if (command == "uci") {
print("id name Omnichess");
print("id author Dany Thach");
print("uciok");
return true;
}
if (command == "debug") {
debug = "on" == inputComponents.first;
return true;
}
if (command == "isready") {
print("readyok");
return true;
}
if (command == "ucinewgame") {
positionCommand = "startpos";
return true;
}
if (command == "position") {
_positionCommand(inputComponents);
return true;
}
if (command == "go") {
await _goCommand(inputComponents);
return true;
}
if (command == "stop") {
await _stopCommand();
return true;
}
if (command == "quit") {
return false;
}
return true;
}
void debugPrintPosition() {
final Position position = Position.fromPositionCommand(positionCommand);
for (int i = 0; i < position.board.length; i++) {
if (i % 8 == 0) {
stdout.write("info string ");
}
stdout.write(Piece.fenCharacterByPiece[position.board[i]]);
if (i % 8 == 7) {
stdout.writeln();
}
}
print(position.isWhiteTurn ? "info string white to move" : "info string black to move");
if (position.canWhiteCastleKingside) {
print("info string white can castle kingside");
}
if (position.canWhiteCastleQueenside) {
print("info string white can castle queenside");
}
if (position.canBlackCastleKingside) {
print("info string black can castle kingside");
}
if (position.canBlackCastleQueenside) {
print("info string black can castle queenside");
}
if (null != position.enPassantTargetSquare) {
print("info string en passant possible on ${position.enPassantTargetSquareAlgebraic}");
}
print("info string halfmove clock ${position.halfmoveClock}");
print("info string fullmove number ${position.fullmoveNumber}");
print("info string position fen ${position.fen}");
}
void loop() async {
late StreamSubscription<String> inputSubscription;
inputSubscription = stdin
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((String line) async {
final String input = line.trim();
final bool keepGoing = await elaborate(input);
if (!keepGoing) {
await inputSubscription.cancel();
}
if (debug) {
debugPrintPosition();
}
});
await inputSubscription.asFuture();
}
}

402
pubspec.lock Normal file
View File

@ -0,0 +1,402 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "03f6da266a27a4538a69295ec142cb5717d7d4e5727b84658b63e1e1509bac9c"
url: "https://pub.dev"
source: hosted
version: "79.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: c9040fc56483c22a5e04a9f6a251313118b1a3c42423770623128fa484115643
url: "https://pub.dev"
source: hosted
version: "7.2.0"
args:
dependency: transitive
description:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.6.0"
async:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev"
source: hosted
version: "2.12.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
coverage:
dependency: transitive
description:
name: coverage
sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43
url: "https://pub.dev"
source: hosted
version: "1.11.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.3"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.5"
js:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
source: hosted
version: "0.7.1"
lints:
dependency: "direct dev"
description:
name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.dev"
source: hosted
version: "5.1.1"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev"
source: hosted
version: "0.12.17"
meta:
dependency: "direct main"
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev"
source: hosted
version: "1.16.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
node_preamble:
dependency: transitive
description:
name: node_preamble
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
package_config:
dependency: transitive
description:
name: package_config
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
url: "https://pub.dev"
source: hosted
version: "1.1.3"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.dev"
source: hosted
version: "2.0.1"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
url: "https://pub.dev"
source: hosted
version: "2.1.2"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
url: "https://pub.dev"
source: hosted
version: "0.10.13"
source_span:
dependency: transitive
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
url: "https://pub.dev"
source: hosted
version: "1.10.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
test:
dependency: "direct dev"
description:
name: test
sha256: "8391fbe68d520daf2314121764d38e37f934c02fd7301ad18307bd93bd6b725d"
url: "https://pub.dev"
source: hosted
version: "1.25.14"
test_api:
dependency: transitive
description:
name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.dev"
source: hosted
version: "0.7.4"
test_core:
dependency: transitive
description:
name: test_core
sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
url: "https://pub.dev"
source: hosted
version: "0.6.8"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "15.0.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
web:
dependency: transitive
description:
name: web
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.6.1 <4.0.0"

15
pubspec.yaml Normal file
View File

@ -0,0 +1,15 @@
name: omnichess
description: A sample command-line application.
version: 1.0.0
# repository: https://github.com/my_org/my_repo
environment:
sdk: ^3.6.1
# Add regular dependencies here.
dependencies:
meta: ^1.16.0
dev_dependencies:
lints: ^5.0.0
test: ^1.24.0