├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── 100.png │ │ │ ├── 114.png │ │ │ ├── 120.png │ │ │ ├── 128.png │ │ │ ├── 144.png │ │ │ ├── 152.png │ │ │ ├── 16.png │ │ │ ├── 167.png │ │ │ ├── 172.png │ │ │ ├── 180.png │ │ │ ├── 196.png │ │ │ ├── 20.png │ │ │ ├── 216.png │ │ │ ├── 256.png │ │ │ ├── 29.png │ │ │ ├── 32.png │ │ │ ├── 40.png │ │ │ ├── 48.png │ │ │ ├── 50.png │ │ │ ├── 512.png │ │ │ ├── 55.png │ │ │ ├── 57.png │ │ │ ├── 58.png │ │ │ ├── 60.png │ │ │ ├── 64.png │ │ │ ├── 72.png │ │ │ ├── 76.png │ │ │ ├── 80.png │ │ │ ├── 87.png │ │ │ ├── 88.png │ │ │ ├── 1024.png │ │ │ └── Contents.json │ │ └── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ ├── Runner.entitlements │ ├── AppDelegate.swift │ ├── Info.plist │ └── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard ├── build │ └── XCBuildData │ │ ├── build.db │ │ ├── 10384adf67b739ef6ccb4819b7a9ee33-desc.xcbuild │ │ ├── 31509be1ca8a7dbad0e96e4f30d83a0b-desc.xcbuild │ │ ├── 31c3a98e90579f090f39295b8569b992-desc.xcbuild │ │ ├── a72cf2a1d171c9732e1ecc72dd2b5a94-desc.xcbuild │ │ └── BuildDescriptionCacheIndex-79501a06438c4d881c8e99681e89acaa ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── project.pbxproj ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Podfile.lock └── Podfile ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ └── Icon-512.png ├── manifest.json └── index.html ├── assets ├── icons │ ├── icon.png │ ├── icon_background.png │ └── icon_foreground.png ├── images │ ├── logo.png │ └── pieces │ │ ├── 8-bit │ │ ├── king_black.png │ │ ├── king_white.png │ │ ├── pawn_black.png │ │ ├── pawn_white.png │ │ ├── rook_black.png │ │ ├── rook_white.png │ │ ├── bishop_black.png │ │ ├── bishop_white.png │ │ ├── knight_black.png │ │ ├── knight_white.png │ │ ├── queen_black.png │ │ └── queen_white.png │ │ ├── angular │ │ ├── king_black.png │ │ ├── king_white.png │ │ ├── pawn_black.png │ │ ├── pawn_white.png │ │ ├── rook_black.png │ │ ├── rook_white.png │ │ ├── bishop_black.png │ │ ├── bishop_white.png │ │ ├── knight_black.png │ │ ├── knight_white.png │ │ ├── queen_black.png │ │ └── queen_white.png │ │ ├── classic │ │ ├── king_black.png │ │ ├── king_white.png │ │ ├── pawn_black.png │ │ ├── pawn_white.png │ │ ├── rook_black.png │ │ ├── rook_white.png │ │ ├── bishop_black.png │ │ ├── bishop_white.png │ │ ├── knight_black.png │ │ ├── knight_white.png │ │ ├── queen_black.png │ │ └── queen_white.png │ │ ├── letters │ │ ├── king_black.png │ │ ├── king_white.png │ │ ├── pawn_black.png │ │ ├── pawn_white.png │ │ ├── rook_black.png │ │ ├── rook_white.png │ │ ├── bishop_black.png │ │ ├── bishop_white.png │ │ ├── knight_black.png │ │ ├── knight_white.png │ │ ├── queen_black.png │ │ └── queen_white.png │ │ ├── mexicocity │ │ ├── king_black.png │ │ ├── king_white.png │ │ ├── pawn_black.png │ │ ├── pawn_white.png │ │ ├── rook_black.png │ │ ├── rook_white.png │ │ ├── bishop_black.png │ │ ├── bishop_white.png │ │ ├── knight_black.png │ │ ├── knight_white.png │ │ ├── queen_black.png │ │ └── queen_white.png │ │ ├── videochess │ │ ├── king_black.png │ │ ├── king_white.png │ │ ├── pawn_black.png │ │ ├── pawn_white.png │ │ ├── rook_black.png │ │ ├── rook_white.png │ │ ├── bishop_black.png │ │ ├── bishop_white.png │ │ ├── knight_black.png │ │ ├── knight_white.png │ │ ├── queen_black.png │ │ └── queen_white.png │ │ └── lewischessmen │ │ ├── bishop_black.png │ │ ├── bishop_white.png │ │ ├── king_black.png │ │ ├── king_white.png │ │ ├── knight_black.png │ │ ├── knight_white.png │ │ ├── pawn_black.png │ │ ├── pawn_white.png │ │ ├── queen_black.png │ │ ├── queen_white.png │ │ ├── rook_black.png │ │ └── rook_white.png ├── audio │ └── piece_moved.mp3 └── font │ └── Jura-VariableFont_wght.ttf ├── lib ├── logic │ ├── move_calculation │ │ ├── move_classes │ │ │ ├── move_and_value.dart │ │ │ ├── direction.dart │ │ │ ├── move.dart │ │ │ ├── move_stack_object.dart │ │ │ └── move_meta.dart │ │ ├── openings.dart │ │ ├── ai_move_calculation.dart │ │ ├── piece_square_tables.dart │ │ └── move_calculation.dart │ ├── chess_piece.dart │ ├── shared_functions.dart │ ├── chess_piece_sprite.dart │ ├── chess_game.dart │ └── chess_board.dart ├── views │ ├── components │ │ ├── shared │ │ │ ├── bottom_padding.dart │ │ │ ├── rounded_button.dart │ │ │ └── text_variable.dart │ │ ├── main_menu_view │ │ │ ├── game_options │ │ │ │ ├── game_mode_picker.dart │ │ │ │ ├── ai_difficulty_picker.dart │ │ │ │ ├── side_picker.dart │ │ │ │ ├── time_limit_picker.dart │ │ │ │ └── picker.dart │ │ │ ├── game_options.dart │ │ │ └── main_menu_buttons.dart │ │ ├── settings_view │ │ │ ├── toggle.dart │ │ │ ├── toggles.dart │ │ │ ├── app_theme_picker.dart │ │ │ ├── piece_preview.dart │ │ │ └── piece_theme_picker.dart │ │ └── chess_view │ │ │ ├── game_info_and_controls │ │ │ ├── moves_undo_redo_row │ │ │ │ ├── rounded_icon_button.dart │ │ │ │ ├── undo_redo_buttons.dart │ │ │ │ └── move_list.dart │ │ │ ├── restart_exit_buttons.dart │ │ │ ├── timers.dart │ │ │ ├── moves_undo_redo_row.dart │ │ │ ├── rounded_alert_button.dart │ │ │ ├── timer_widget.dart │ │ │ └── game_status.dart │ │ │ ├── promotion_dialog.dart │ │ │ ├── promotion_option.dart │ │ │ ├── game_info_and_controls.dart │ │ │ └── chess_board_widget.dart │ ├── main_menu_view.dart │ ├── settings_view.dart │ └── chess_view.dart ├── main.dart └── model │ ├── app_model.dart │ └── app_themes.dart ├── .metadata ├── .gitignore ├── README.md └── pubspec.yaml /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/web/favicon.png -------------------------------------------------------------------------------- /assets/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/icons/icon.png -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/logo.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /assets/audio/piece_moved.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/audio/piece_moved.mp3 -------------------------------------------------------------------------------- /ios/build/XCBuildData/build.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/build/XCBuildData/build.db -------------------------------------------------------------------------------- /assets/icons/icon_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/icons/icon_background.png -------------------------------------------------------------------------------- /assets/icons/icon_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/icons/icon_foreground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /assets/font/Jura-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/font/Jura-VariableFont_wght.ttf -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/king_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/king_black.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/king_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/king_white.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/pawn_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/pawn_black.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/pawn_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/pawn_white.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/rook_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/rook_black.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/rook_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/rook_white.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/bishop_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/bishop_black.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/bishop_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/bishop_white.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/knight_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/knight_black.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/knight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/knight_white.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/queen_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/queen_black.png -------------------------------------------------------------------------------- /assets/images/pieces/8-bit/queen_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/8-bit/queen_white.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/king_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/king_black.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/king_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/king_white.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/pawn_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/pawn_black.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/pawn_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/pawn_white.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/rook_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/rook_black.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/rook_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/rook_white.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/king_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/king_black.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/king_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/king_white.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/pawn_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/pawn_black.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/pawn_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/pawn_white.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/rook_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/rook_black.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/rook_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/rook_white.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/king_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/king_black.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/king_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/king_white.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/pawn_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/pawn_black.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/pawn_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/pawn_white.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/rook_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/rook_black.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/rook_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/rook_white.png -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/images/pieces/angular/bishop_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/bishop_black.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/bishop_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/bishop_white.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/knight_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/knight_black.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/knight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/knight_white.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/queen_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/queen_black.png -------------------------------------------------------------------------------- /assets/images/pieces/angular/queen_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/angular/queen_white.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/bishop_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/bishop_black.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/bishop_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/bishop_white.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/knight_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/knight_black.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/knight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/knight_white.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/queen_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/queen_black.png -------------------------------------------------------------------------------- /assets/images/pieces/classic/queen_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/classic/queen_white.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/bishop_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/bishop_black.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/bishop_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/bishop_white.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/knight_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/knight_black.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/knight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/knight_white.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/queen_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/queen_black.png -------------------------------------------------------------------------------- /assets/images/pieces/letters/queen_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/letters/queen_white.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/king_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/king_black.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/king_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/king_white.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/pawn_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/pawn_black.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/pawn_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/pawn_white.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/rook_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/rook_black.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/rook_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/rook_white.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/king_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/king_black.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/king_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/king_white.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/pawn_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/pawn_black.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/pawn_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/pawn_white.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/rook_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/rook_black.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/rook_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/rook_white.png -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/bishop_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/bishop_black.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/bishop_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/bishop_white.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/knight_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/knight_black.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/knight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/knight_white.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/queen_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/queen_black.png -------------------------------------------------------------------------------- /assets/images/pieces/mexicocity/queen_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/mexicocity/queen_white.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/bishop_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/bishop_black.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/bishop_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/bishop_white.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/knight_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/knight_black.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/knight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/knight_white.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/queen_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/queen_black.png -------------------------------------------------------------------------------- /assets/images/pieces/videochess/queen_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/videochess/queen_white.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/bishop_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/bishop_black.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/bishop_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/bishop_white.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/king_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/king_black.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/king_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/king_white.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/knight_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/knight_black.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/knight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/knight_white.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/pawn_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/pawn_black.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/pawn_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/pawn_white.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/queen_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/queen_black.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/queen_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/queen_white.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/rook_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/rook_black.png -------------------------------------------------------------------------------- /assets/images/pieces/lewischessmen/rook_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/assets/images/pieces/lewischessmen/rook_white.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/172.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/172.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/196.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/216.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/48.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/55.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/88.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/build/XCBuildData/10384adf67b739ef6ccb4819b7a9ee33-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/build/XCBuildData/10384adf67b739ef6ccb4819b7a9ee33-desc.xcbuild -------------------------------------------------------------------------------- /ios/build/XCBuildData/31509be1ca8a7dbad0e96e4f30d83a0b-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/build/XCBuildData/31509be1ca8a7dbad0e96e4f30d83a0b-desc.xcbuild -------------------------------------------------------------------------------- /ios/build/XCBuildData/31c3a98e90579f090f39295b8569b992-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/build/XCBuildData/31c3a98e90579f090f39295b8569b992-desc.xcbuild -------------------------------------------------------------------------------- /ios/build/XCBuildData/a72cf2a1d171c9732e1ecc72dd2b5a94-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/build/XCBuildData/a72cf2a1d171c9732e1ecc72dd2b5a94-desc.xcbuild -------------------------------------------------------------------------------- /lib/logic/move_calculation/move_classes/move_and_value.dart: -------------------------------------------------------------------------------- 1 | import 'move.dart'; 2 | 3 | class MoveAndValue { 4 | Move move; 5 | int value; 6 | 7 | MoveAndValue(this.move, this.value); 8 | } 9 | -------------------------------------------------------------------------------- /ios/build/XCBuildData/BuildDescriptionCacheIndex-79501a06438c4d881c8e99681e89acaa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PScottZero/EnPassant/HEAD/ios/build/XCBuildData/BuildDescriptionCacheIndex-79501a06438c4d881c8e99681e89acaa -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 78910062997c3a836feee883712c241a5fd22983 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /lib/logic/move_calculation/move_classes/direction.dart: -------------------------------------------------------------------------------- 1 | class Direction { 2 | final int up; 3 | final int right; 4 | 5 | const Direction(this.up, this.right); 6 | } 7 | 8 | const UP = Direction(1, 0); 9 | const UP_RIGHT = Direction(1, 1); 10 | const RIGHT = Direction(0, 1); 11 | const DOWN_RIGHT = Direction(-1, 1); 12 | const DOWN = Direction(-1, 0); 13 | const DOWN_LEFT = Direction(-1, -1); 14 | const LEFT = Direction(0, -1); 15 | const UP_LEFT = Direction(1, -1); 16 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/logic/move_calculation/move_classes/move.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/logic/chess_piece.dart'; 2 | 3 | class Move { 4 | int from; 5 | int to; 6 | ChessPieceType promotionType; 7 | 8 | Move(this.from, this.to, {this.promotionType = ChessPieceType.promotion}); 9 | 10 | @override 11 | bool operator ==(move) => 12 | this.from == (move as Move).from && (this as Move).to == move.to; 13 | 14 | @override 15 | int get hashCode => super.hashCode; 16 | } 17 | -------------------------------------------------------------------------------- /lib/views/components/shared/bottom_padding.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/cupertino.dart'; 5 | 6 | class BottomPadding extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return SizedBox( 10 | height: Platform.isAndroid 11 | ? max(MediaQuery.of(context).viewInsets.bottom, 12 | MediaQuery.of(context).padding.bottom) 13 | : 0, 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/logic/move_calculation/move_classes/move_stack_object.dart: -------------------------------------------------------------------------------- 1 | import '../../chess_piece.dart'; 2 | import 'move.dart'; 3 | 4 | class MoveStackObject { 5 | Move move; 6 | ChessPiece? movedPiece; 7 | ChessPiece? takenPiece; 8 | ChessPiece? enPassantPiece; 9 | bool castled = false; 10 | bool promotion = false; 11 | ChessPieceType? promotionType; 12 | bool enPassant = false; 13 | List>? possibleOpenings; 14 | 15 | MoveStackObject(this.move, this.movedPiece, this.takenPiece, 16 | this.enPassantPiece, this.possibleOpenings); 17 | } 18 | -------------------------------------------------------------------------------- /lib/logic/move_calculation/move_classes/move_meta.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 2 | 3 | import '../../chess_piece.dart'; 4 | import 'move.dart'; 5 | 6 | class MoveMeta { 7 | Move? move; 8 | Player? player; 9 | ChessPieceType? type; 10 | bool took = false; 11 | bool kingCastle = false; 12 | bool queenCastle = false; 13 | bool promotion = false; 14 | ChessPieceType? promotionType; 15 | bool isCheck = false; 16 | bool isCheckmate = false; 17 | bool isStalemate = false; 18 | bool rowIsAmbiguous = false; 19 | bool colIsAmbiguous = false; 20 | 21 | MoveMeta(this.move, this.player, this.type); 22 | } 23 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "en_passant", 3 | "short_name": "en_passant", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /lib/views/components/main_menu_view/game_options/game_mode_picker.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | import 'picker.dart'; 4 | 5 | class GameModePicker extends StatelessWidget { 6 | final Map playerCountOptions = const { 7 | 1: Text('One Player'), 8 | 2: Text('Two Player') 9 | }; 10 | 11 | final int playerCount; 12 | final Function(int?) setFunc; 13 | 14 | GameModePicker(this.playerCount, this.setFunc); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Picker( 19 | label: 'Game Mode', 20 | options: playerCountOptions, 21 | selection: playerCount, 22 | setFunc: setFunc, 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/views/components/settings_view/toggle.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | import '../shared/text_variable.dart'; 4 | 5 | class Toggle extends StatelessWidget { 6 | final String label; 7 | final bool? toggle; 8 | final Function(bool)? setFunc; 9 | 10 | Toggle(this.label, {this.toggle, this.setFunc}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | height: 55, 16 | child: Row( 17 | children: [ 18 | TextRegular(label), 19 | Spacer(), 20 | CupertinoSwitch( 21 | value: toggle ?? false, 22 | onChanged: setFunc, 23 | ), 24 | ], 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls/moves_undo_redo_row/rounded_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class RoundedIconButton extends StatelessWidget { 4 | final IconData icon; 5 | final void Function()? onPressed; 6 | 7 | RoundedIconButton(this.icon, {this.onPressed}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Container( 12 | child: CupertinoButton( 13 | padding: EdgeInsets.zero, 14 | color: Color(0x20000000), 15 | child: Icon(icon, color: Color(0xffffffff)), 16 | borderRadius: BorderRadius.all(Radius.circular(15)), 17 | onPressed: onPressed, 18 | ), 19 | width: double.infinity, 20 | height: 60, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/views/components/main_menu_view/game_options/ai_difficulty_picker.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | import 'picker.dart'; 4 | 5 | class AIDifficultyPicker extends StatelessWidget { 6 | final Map difficultyOptions = { 7 | 1: Text('1'), 8 | 2: Text('2'), 9 | 3: Text('3'), 10 | 4: Text('4'), 11 | 5: Text('5'), 12 | 6: Text('6') 13 | }; 14 | 15 | final int aiDifficulty; 16 | final Function(int?) setFunc; 17 | 18 | AIDifficultyPicker(this.aiDifficulty, this.setFunc); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Picker( 23 | label: 'AI Difficulty', 24 | options: difficultyOptions, 25 | selection: aiDifficulty, 26 | setFunc: setFunc, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/views/components/main_menu_view/game_options/side_picker.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | import 'picker.dart'; 4 | 5 | enum Player { player1, player2, random } 6 | 7 | class SidePicker extends StatelessWidget { 8 | final Map colorOptions = const { 9 | Player.player1: Text('White'), 10 | Player.player2: Text('Black'), 11 | Player.random: Text('Random') 12 | }; 13 | 14 | final Player playerSide; 15 | final Function(Player?) setFunc; 16 | 17 | SidePicker(this.playerSide, this.setFunc); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Picker( 22 | label: 'Side', 23 | options: colorOptions, 24 | selection: playerSide, 25 | setFunc: setFunc, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | -------------------------------------------------------------------------------- /lib/views/components/main_menu_view/game_options/time_limit_picker.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/views/components/main_menu_view/game_options/picker.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | class TimeLimitPicker extends StatelessWidget { 5 | final int? selectedTime; 6 | final Function(int?)? setTime; 7 | 8 | TimeLimitPicker({this.selectedTime, this.setTime}); 9 | 10 | final Map timeOptions = const { 11 | 0: Text('None'), 12 | 15: Text('15m'), 13 | 30: Text('30m'), 14 | 60: Text('1h'), 15 | 90: Text('1.5h'), 16 | 120: Text('2h') 17 | }; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Picker( 22 | label: 'Time Limit', 23 | options: timeOptions, 24 | selection: selectedTime, 25 | setFunc: setTime, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/views/components/shared/rounded_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class RoundedButton extends StatelessWidget { 5 | final String label; 6 | final Function() onPressed; 7 | 8 | RoundedButton(this.label, {required this.onPressed}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Container( 13 | child: CupertinoButton( 14 | padding: EdgeInsets.zero, 15 | color: Color(0x20000000), 16 | child: Text( 17 | label, 18 | style: TextStyle( 19 | color: Colors.white, 20 | fontSize: 24, 21 | ), 22 | ), 23 | borderRadius: BorderRadius.all(Radius.circular(15)), 24 | onPressed: onPressed, 25 | ), 26 | width: double.infinity, 27 | height: 60, 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls/restart_exit_buttons.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/chess_view/game_info_and_controls/rounded_alert_button.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | class RestartExitButtons extends StatelessWidget { 6 | final AppModel appModel; 7 | 8 | RestartExitButtons(this.appModel); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Row( 13 | children: [ 14 | Expanded( 15 | child: RoundedAlertButton( 16 | 'Restart', 17 | onConfirm: () { 18 | appModel.newGame(context); 19 | }, 20 | ), 21 | ), 22 | SizedBox(width: 10), 23 | Expanded( 24 | child: RoundedAlertButton( 25 | 'Exit', 26 | onConfirm: () { 27 | appModel.exitChessView(); 28 | Navigator.pop(context); 29 | }, 30 | ), 31 | ), 32 | ], 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/promotion_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/logic/chess_piece.dart'; 2 | import 'package:en_passant/model/app_model.dart'; 3 | import 'package:en_passant/views/components/chess_view/promotion_option.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | 6 | const PROMOTIONS = [ 7 | ChessPieceType.queen, 8 | ChessPieceType.rook, 9 | ChessPieceType.bishop, 10 | ChessPieceType.knight 11 | ]; 12 | 13 | class PromotionDialog extends StatelessWidget { 14 | final AppModel appModel; 15 | 16 | PromotionDialog(this.appModel); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return CupertinoAlertDialog( 21 | actions: [ 22 | Container( 23 | height: 66, 24 | child: Row( 25 | children: PROMOTIONS 26 | .map( 27 | (promotionType) => PromotionOption( 28 | appModel, 29 | promotionType, 30 | ), 31 | ) 32 | .toList(), 33 | ), 34 | ), 35 | ], 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/promotion_option.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/logic/chess_piece.dart'; 2 | import 'package:en_passant/logic/shared_functions.dart'; 3 | import 'package:en_passant/model/app_model.dart'; 4 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | 7 | class PromotionOption extends StatelessWidget { 8 | final AppModel appModel; 9 | final ChessPieceType promotionType; 10 | 11 | PromotionOption(this.appModel, this.promotionType); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return CupertinoButton( 16 | child: Image( 17 | image: AssetImage( 18 | 'assets/images/pieces/${formatPieceTheme(appModel.pieceTheme)}' + 19 | '/${pieceTypeToString(promotionType)}_${_playerColor()}.png', 20 | ), 21 | ), 22 | onPressed: () { 23 | appModel.game?.promote(promotionType); 24 | appModel.update(); 25 | Navigator.pop(context); 26 | }, 27 | ); 28 | } 29 | 30 | String _playerColor() { 31 | return appModel.turn == Player.player1 ? 'white' : 'black'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls/timers.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/chess_view/game_info_and_controls/timer_widget.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class Timers extends StatelessWidget { 6 | final AppModel appModel; 7 | 8 | Timers(this.appModel); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return appModel.timeLimit != 0 13 | ? Column( 14 | children: [ 15 | Container( 16 | child: Row( 17 | children: [ 18 | TimerWidget( 19 | timeLeft: appModel.player1TimeLeft, 20 | color: Colors.white, 21 | ), 22 | SizedBox(width: 10), 23 | TimerWidget( 24 | timeLeft: appModel.player2TimeLeft, 25 | color: Colors.black, 26 | ), 27 | ], 28 | ), 29 | ), 30 | SizedBox(height: 14), 31 | ], 32 | ) 33 | : Container(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls/moves_undo_redo_row.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/chess_view/game_info_and_controls/moves_undo_redo_row/undo_redo_buttons.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | import 'moves_undo_redo_row/move_list.dart'; 6 | 7 | class MovesUndoRedoRow extends StatelessWidget { 8 | final AppModel appModel; 9 | 10 | MovesUndoRedoRow(this.appModel); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Column( 15 | children: [ 16 | Row( 17 | children: [ 18 | appModel.showMoveHistory 19 | ? Expanded(child: MoveList(appModel)) 20 | : Container(), 21 | appModel.showMoveHistory && appModel.allowUndoRedo 22 | ? SizedBox(width: 10) 23 | : Container(), 24 | appModel.allowUndoRedo 25 | ? Expanded(child: UndoRedoButtons(appModel)) 26 | : Container(), 27 | ], 28 | ), 29 | appModel.showMoveHistory || appModel.allowUndoRedo 30 | ? SizedBox(height: 10) 31 | : Container(), 32 | ], 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/views/components/shared/text_variable.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class TextDefault extends StatelessWidget { 4 | final String text; 5 | final Color color; 6 | 7 | TextDefault(this.text, {this.color = CupertinoColors.white}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Text( 12 | text, 13 | style: TextStyle( 14 | fontSize: 16, 15 | fontFamily: 'Jura', 16 | color: color, 17 | ), 18 | ); 19 | } 20 | } 21 | 22 | class TextSmall extends StatelessWidget { 23 | final String text; 24 | 25 | TextSmall(this.text); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Text(text, style: TextStyle(fontSize: 20)); 30 | } 31 | } 32 | 33 | class TextRegular extends StatelessWidget { 34 | final String text; 35 | 36 | TextRegular(this.text); 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Text(text, style: TextStyle(fontSize: 24)); 41 | } 42 | } 43 | 44 | class TextLarge extends StatelessWidget { 45 | final String text; 46 | 47 | TextLarge(this.text); 48 | 49 | @override 50 | Widget build(BuildContext context) { 51 | return Text(text, style: TextStyle(fontSize: 36)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/logic/chess_piece.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 2 | 3 | enum ChessPieceType { pawn, rook, knight, bishop, king, queen, promotion } 4 | 5 | class ChessPiece { 6 | int id; 7 | ChessPieceType type; 8 | Player player; 9 | int moveCount = 0; 10 | int tile; 11 | 12 | int get value { 13 | int value = 0; 14 | switch (type) { 15 | case ChessPieceType.pawn: 16 | { 17 | value = 100; 18 | } 19 | break; 20 | case ChessPieceType.knight: 21 | { 22 | value = 320; 23 | } 24 | break; 25 | case ChessPieceType.bishop: 26 | { 27 | value = 330; 28 | } 29 | break; 30 | case ChessPieceType.rook: 31 | { 32 | value = 500; 33 | } 34 | break; 35 | case ChessPieceType.queen: 36 | { 37 | value = 900; 38 | } 39 | break; 40 | case ChessPieceType.king: 41 | { 42 | value = 20000; 43 | } 44 | break; 45 | default: 46 | { 47 | value = 0; 48 | } 49 | } 50 | return (player == Player.player1) ? value : -value; 51 | } 52 | 53 | ChessPiece(this.id, this.type, this.player, this.tile); 54 | } 55 | -------------------------------------------------------------------------------- /lib/logic/shared_functions.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/logic/chess_piece.dart'; 2 | import 'package:en_passant/model/app_model.dart'; 3 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 4 | 5 | int tileToRow(int tile) { 6 | return (tile / 8).floor(); 7 | } 8 | 9 | int tileToCol(int tile) { 10 | return tile % 8; 11 | } 12 | 13 | double getXFromTile(int tile, double tileSize, AppModel appModel) { 14 | return appModel.flip && 15 | appModel.playingWithAI && 16 | appModel.playerSide == Player.player2 17 | ? (7 - tileToCol(tile)) * tileSize 18 | : tileToCol(tile) * tileSize; 19 | } 20 | 21 | double getYFromTile(int tile, double tileSize, AppModel appModel) { 22 | return appModel.flip && 23 | appModel.playingWithAI && 24 | appModel.playerSide == Player.player2 25 | ? (7 - tileToRow(tile)) * tileSize 26 | : tileToRow(tile) * tileSize; 27 | } 28 | 29 | Player oppositePlayer(Player player) { 30 | return player == Player.player1 ? Player.player2 : Player.player1; 31 | } 32 | 33 | String formatPieceTheme(String themeString) { 34 | return themeString.toLowerCase().replaceAll(' ', ''); 35 | } 36 | 37 | String pieceTypeToString(ChessPieceType type) { 38 | return type.toString().substring(type.toString().indexOf('.') + 1); 39 | } 40 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | import 'game_info_and_controls/moves_undo_redo_row.dart'; 5 | import 'game_info_and_controls/restart_exit_buttons.dart'; 6 | import 'game_info_and_controls/timers.dart'; 7 | 8 | class GameInfoAndControls extends StatelessWidget { 9 | final AppModel appModel; 10 | final ScrollController scrollController = ScrollController(); 11 | 12 | GameInfoAndControls(this.appModel); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom()); 17 | return Container( 18 | constraints: BoxConstraints( 19 | maxHeight: MediaQuery.of(context).size.height > 700 ? 204 : 134, 20 | ), 21 | child: ListView( 22 | controller: scrollController, 23 | physics: ClampingScrollPhysics(), 24 | shrinkWrap: true, 25 | padding: EdgeInsets.zero, 26 | children: [ 27 | Timers(appModel), 28 | MovesUndoRedoRow(appModel), 29 | RestartExitButtons(appModel), 30 | ], 31 | ), 32 | ); 33 | } 34 | 35 | void _scrollToBottom() { 36 | scrollController.jumpTo(scrollController.position.maxScrollExtent); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/views/components/settings_view/toggles.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:en_passant/model/app_model.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'toggle.dart'; 7 | 8 | class Toggles extends StatelessWidget { 9 | final AppModel appModel; 10 | 11 | Toggles(this.appModel); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Column( 16 | children: [ 17 | Toggle( 18 | 'Show Hints', 19 | toggle: appModel.showHints, 20 | setFunc: appModel.setShowHints, 21 | ), 22 | Toggle( 23 | 'Allow Undo/Redo', 24 | toggle: appModel.allowUndoRedo, 25 | setFunc: appModel.setAllowUndoRedo, 26 | ), 27 | Toggle( 28 | 'Show Move History', 29 | toggle: appModel.showMoveHistory, 30 | setFunc: appModel.setShowMoveHistory, 31 | ), 32 | Toggle( 33 | 'Flip Board For Black', 34 | toggle: appModel.flip, 35 | setFunc: appModel.setFlipBoard, 36 | ), 37 | Platform.isAndroid 38 | ? Toggle( 39 | 'Sound Enabled', 40 | toggle: appModel.soundEnabled, 41 | setFunc: appModel.setSoundEnabled, 42 | ) 43 | : Container(), 44 | ], 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - audioplayers (0.0.1): 3 | - Flutter 4 | - Flutter (1.0.0) 5 | - path_provider (0.0.1): 6 | - Flutter 7 | - shared_preferences (0.0.1): 8 | - Flutter 9 | - url_launcher (0.0.1): 10 | - Flutter 11 | 12 | DEPENDENCIES: 13 | - audioplayers (from `.symlinks/plugins/audioplayers/ios`) 14 | - Flutter (from `Flutter`) 15 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 16 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 17 | - url_launcher (from `.symlinks/plugins/url_launcher/ios`) 18 | 19 | EXTERNAL SOURCES: 20 | audioplayers: 21 | :path: ".symlinks/plugins/audioplayers/ios" 22 | Flutter: 23 | :path: Flutter 24 | path_provider: 25 | :path: ".symlinks/plugins/path_provider/ios" 26 | shared_preferences: 27 | :path: ".symlinks/plugins/shared_preferences/ios" 28 | url_launcher: 29 | :path: ".symlinks/plugins/url_launcher/ios" 30 | 31 | SPEC CHECKSUMS: 32 | audioplayers: 455322b54050b30ea4b1af7cd9e9d105f74efa8c 33 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c 34 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c 35 | shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d 36 | url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef 37 | 38 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 39 | 40 | COCOAPODS: 1.10.1 41 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/chess_board_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:flame/game.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | class ChessBoardWidget extends StatelessWidget { 6 | final AppModel appModel; 7 | 8 | ChessBoardWidget(this.appModel); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Container( 13 | decoration: appModel.theme.name != 'Video Chess' 14 | ? BoxDecoration( 15 | border: Border.all( 16 | color: appModel.theme.border, 17 | width: 4, 18 | ), 19 | borderRadius: BorderRadius.circular(14), 20 | boxShadow: [ 21 | BoxShadow( 22 | blurRadius: 10, 23 | color: Color(0x88000000), 24 | ), 25 | ], 26 | ) 27 | : BoxDecoration(), 28 | child: ClipRRect( 29 | borderRadius: appModel.theme.name != 'Video Chess' 30 | ? BorderRadius.circular(10) 31 | : BorderRadius.zero, 32 | child: Container( 33 | child: GameWidget(game: appModel.game!), 34 | width: MediaQuery.of(context).size.width - 68, 35 | height: MediaQuery.of(context).size.width - 68, 36 | ), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/views/components/main_menu_view/game_options/picker.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | import '../../shared/text_variable.dart'; 4 | 5 | class Picker extends StatelessWidget { 6 | final String? label; 7 | final Map? options; 8 | final T? selection; 9 | final Function(T?)? setFunc; 10 | 11 | Picker({this.label, this.options, this.selection, this.setFunc}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Column( 16 | children: [ 17 | TextSmall(label ?? ""), 18 | SizedBox(height: 10), 19 | Container( 20 | child: CupertinoTheme( 21 | data: CupertinoThemeData( 22 | textTheme: CupertinoTextThemeData( 23 | textStyle: TextStyle(fontFamily: 'Jura', fontSize: 8), 24 | ), 25 | ), 26 | child: CupertinoSlidingSegmentedControl( 27 | children: options ?? {}, 28 | groupValue: selection, 29 | onValueChanged: (T? val) { 30 | if (setFunc != null) { 31 | setFunc!(val); 32 | } 33 | }, 34 | thumbColor: Color(0x88FFFFFF), 35 | backgroundColor: Color(0x20000000), 36 | ), 37 | ), 38 | width: double.infinity, 39 | ) 40 | ], 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/views/main_menu_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/main_menu_view/game_options.dart'; 3 | import 'package:en_passant/views/components/shared/bottom_padding.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | import 'components/main_menu_view/main_menu_buttons.dart'; 8 | 9 | class MainMenuView extends StatefulWidget { 10 | @override 11 | _MainMenuViewState createState() => _MainMenuViewState(); 12 | } 13 | 14 | class _MainMenuViewState extends State { 15 | @override 16 | Widget build(BuildContext context) { 17 | return Consumer( 18 | builder: (context, appModel, child) { 19 | return Container( 20 | decoration: BoxDecoration(gradient: appModel.theme.background), 21 | padding: EdgeInsets.all(30), 22 | child: Column( 23 | children: [ 24 | Container( 25 | padding: EdgeInsets.fromLTRB( 26 | 10, MediaQuery.of(context).padding.top + 10, 10, 0), 27 | child: Image.asset('assets/images/logo.png'), 28 | ), 29 | SizedBox(height: 20), 30 | GameOptions(appModel), 31 | SizedBox(height: 10), 32 | MainMenuButtons(appModel), 33 | BottomPadding(), 34 | ], 35 | ), 36 | ); 37 | }, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # En Passant 2 | 3 | En Passant is a chess app written in Flutter using Flame engine. It is a remake of [Checkmate](https://github.com/PScottZero/Checkmate), a SwiftUI project which I submitted as my final project for an application development course I took at Penn State (CMPSC 475) during the fall of 2020. 4 | 5 | Get it on Google Play 6 | 7 | ## Features 8 | - 1 or 2 player gameplay (2 player is offline) 9 | - Six AI difficulty levels 10 | - Customizable app theme 11 | - Customizable piece theme 12 | 13 | ## AI Description 14 | 15 | The chess AI I developed for this app uses the minimax algorithm with alpha-beta pruning to calculate which moves to make. There are six difficulty levels in the app, each level corresponding to the depth of the search used in the minimax algorithm. The highest difficulty is 6, which corresponds to 3 full chess moves. To learn more about how this algorithm works, use the following link: https://en.wikipedia.org/wiki/Alpha–beta_pruning. 16 | 17 | ## Screenshots 18 | 19 | 20 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls/rounded_alert_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | import '../../shared/rounded_button.dart'; 4 | import '../../shared/text_variable.dart'; 5 | 6 | class RoundedAlertButton extends StatelessWidget { 7 | final String label; 8 | final Function onConfirm; 9 | 10 | RoundedAlertButton(this.label, {required this.onConfirm}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return RoundedButton(label, onPressed: () { 15 | showCupertinoDialog( 16 | context: context, 17 | builder: (BuildContext context) { 18 | return CupertinoAlertDialog( 19 | title: TextDefault(label), 20 | content: TextDefault( 21 | 'Are you sure you want to ${label.toLowerCase()}?', 22 | ), 23 | actions: [ 24 | CupertinoButton( 25 | child: TextDefault( 26 | label, 27 | color: CupertinoColors.destructiveRed, 28 | ), 29 | onPressed: () { 30 | onConfirm(); 31 | Navigator.pop(context); 32 | }, 33 | ), 34 | CupertinoButton( 35 | child: TextDefault('Cancel', color: CupertinoColors.activeBlue), 36 | onPressed: () { 37 | Navigator.pop(context); 38 | }, 39 | ), 40 | ], 41 | ); 42 | }, 43 | ); 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/main_menu_view.dart'; 3 | import 'package:flame/flame.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | import 'logic/shared_functions.dart'; 9 | 10 | void main() { 11 | runApp( 12 | ChangeNotifierProvider( 13 | create: (context) => AppModel(), 14 | child: EnPassantApp(), 15 | ), 16 | ); 17 | _loadFlameAssets(); 18 | } 19 | 20 | void _loadFlameAssets() async { 21 | List pieceImages = []; 22 | for (var theme in PIECE_THEMES) { 23 | for (var color in ['black', 'white']) { 24 | for (var piece in ['king', 'queen', 'rook', 'bishop', 'knight', 'pawn']) { 25 | pieceImages 26 | .add('pieces/${formatPieceTheme(theme)}/${piece}_$color.png'); 27 | } 28 | } 29 | } 30 | await Flame.images.loadAll(pieceImages); 31 | } 32 | 33 | class EnPassantApp extends StatelessWidget { 34 | @override 35 | Widget build(BuildContext context) { 36 | SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); 37 | return CupertinoApp( 38 | title: 'En Passant', 39 | theme: CupertinoThemeData( 40 | brightness: Brightness.dark, 41 | textTheme: CupertinoTextThemeData( 42 | textStyle: TextStyle(fontFamily: 'Jura', fontSize: 16), 43 | pickerTextStyle: TextStyle(fontFamily: 'Jura'), 44 | ), 45 | ), 46 | home: MainMenuView(), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | En Passant 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | 33 | UISupportedInterfaceOrientations~ipad 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationPortraitUpsideDown 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | UIViewControllerBasedStatusBarAppearance 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls/timer_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/views/components/shared/text_variable.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | class TimerWidget extends StatelessWidget { 5 | final Duration timeLeft; 6 | final Color color; 7 | 8 | TimerWidget({required this.timeLeft, required this.color}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Expanded( 13 | child: Container( 14 | height: 60, 15 | child: Center( 16 | child: TextRegular(_durationToString(timeLeft)), 17 | ), 18 | decoration: BoxDecoration( 19 | border: Border.all(color: color, width: 2), 20 | borderRadius: BorderRadius.circular(14), 21 | color: Color(0x20000000), 22 | ), 23 | ), 24 | ); 25 | } 26 | 27 | String _durationToString(Duration duration) { 28 | if (duration.inHours > 0) { 29 | String hours = duration.inHours.toString(); 30 | String minutes = 31 | duration.inMinutes.remainder(60).toString().padLeft(2, '0'); 32 | String seconds = 33 | duration.inSeconds.remainder(60).toString().padLeft(2, '0'); 34 | return '$hours:$minutes:$seconds'; 35 | } else if (duration.inMinutes > 0) { 36 | String minutes = duration.inMinutes.toString(); 37 | String seconds = 38 | duration.inSeconds.remainder(60).toString().padLeft(2, '0'); 39 | return '$minutes:$seconds'; 40 | } else { 41 | String seconds = duration.inSeconds.toString(); 42 | return '$seconds'; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/views/components/main_menu_view/game_options.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | import 'game_options/ai_difficulty_picker.dart'; 5 | import 'game_options/game_mode_picker.dart'; 6 | import 'game_options/side_picker.dart'; 7 | import 'game_options/time_limit_picker.dart'; 8 | 9 | class GameOptions extends StatelessWidget { 10 | final AppModel appModel; 11 | 12 | GameOptions(this.appModel); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Expanded( 17 | child: ListView( 18 | padding: EdgeInsets.zero, 19 | physics: ClampingScrollPhysics(), 20 | children: [ 21 | GameModePicker( 22 | appModel.playerCount, 23 | appModel.setPlayerCount, 24 | ), 25 | SizedBox(height: 20), 26 | appModel.playerCount == 1 27 | ? Column( 28 | children: [ 29 | AIDifficultyPicker( 30 | appModel.aiDifficulty, 31 | appModel.setAIDifficulty, 32 | ), 33 | SizedBox(height: 20), 34 | SidePicker( 35 | appModel.selectedSide, 36 | appModel.setPlayerSide, 37 | ), 38 | SizedBox(height: 20), 39 | ], 40 | ) 41 | : Container(), 42 | TimeLimitPicker( 43 | selectedTime: appModel.timeLimit, 44 | setTime: appModel.setTimeLimit, 45 | ), 46 | ], 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | en_passant 30 | 31 | 32 | 33 | 36 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/views/components/settings_view/app_theme_picker.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/model/app_themes.dart'; 3 | import 'package:en_passant/views/components/shared/text_variable.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | class AppThemePicker extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Consumer( 11 | builder: (context, appModel, child) => Column( 12 | children: [ 13 | Container( 14 | child: TextSmall('App Theme'), 15 | padding: EdgeInsets.all(10), 16 | ), 17 | Container( 18 | height: 120, 19 | decoration: BoxDecoration( 20 | borderRadius: BorderRadius.all(Radius.circular(15)), 21 | color: Color(0x20000000), 22 | ), 23 | child: CupertinoPicker( 24 | scrollController: FixedExtentScrollController( 25 | initialItem: appModel.themeIndex, 26 | ), 27 | selectionOverlay: CupertinoPickerDefaultSelectionOverlay( 28 | background: Color(0x20000000), 29 | ), 30 | itemExtent: 50, 31 | onSelectedItemChanged: appModel.setTheme, 32 | children: themeList 33 | .map( 34 | (theme) => Container( 35 | padding: EdgeInsets.all(10), 36 | child: TextRegular(theme.name ?? ""), 37 | ), 38 | ) 39 | .toList(), 40 | ), 41 | ), 42 | ], 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/views/settings_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/settings_view/piece_theme_picker.dart'; 3 | import 'package:en_passant/views/components/shared/rounded_button.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | import 'components/settings_view/app_theme_picker.dart'; 8 | import 'components/settings_view/toggles.dart'; 9 | import 'components/shared/bottom_padding.dart'; 10 | import 'components/shared/text_variable.dart'; 11 | 12 | class SettingsView extends StatelessWidget { 13 | @override 14 | Widget build(BuildContext context) { 15 | return Consumer( 16 | builder: (context, appModel, child) => Container( 17 | decoration: BoxDecoration(gradient: appModel.theme.background), 18 | padding: EdgeInsets.all(30), 19 | child: Column( 20 | children: [ 21 | SizedBox(height: MediaQuery.of(context).padding.top), 22 | TextLarge('Settings'), 23 | SizedBox(height: 20), 24 | Expanded( 25 | child: ListView( 26 | padding: EdgeInsets.zero, 27 | physics: ClampingScrollPhysics(), 28 | children: [ 29 | AppThemePicker(), 30 | SizedBox(height: 20), 31 | PieceThemePicker(), 32 | SizedBox(height: 10), 33 | Toggles(appModel), 34 | ], 35 | ), 36 | ), 37 | SizedBox(height: 30), 38 | RoundedButton( 39 | 'Back', 40 | onPressed: () { 41 | Navigator.pop(context); 42 | }, 43 | ), 44 | BottomPadding(), 45 | ], 46 | ), 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/logic/move_calculation/openings.dart: -------------------------------------------------------------------------------- 1 | import 'move_classes/move.dart'; 2 | 3 | var openings = [ 4 | [ 5 | // Ruy Lopez 6 | Move(tileToInt('e2'), tileToInt('e4')), 7 | Move(tileToInt('e7'), tileToInt('e5')), 8 | Move(tileToInt('g1'), tileToInt('f3')), 9 | Move(tileToInt('b8'), tileToInt('c6')), 10 | Move(tileToInt('f1'), tileToInt('b5')), 11 | ], 12 | [ 13 | // Italian Game 14 | Move(tileToInt('e2'), tileToInt('e4')), 15 | Move(tileToInt('e7'), tileToInt('e5')), 16 | Move(tileToInt('g1'), tileToInt('f3')), 17 | Move(tileToInt('b8'), tileToInt('c6')), 18 | Move(tileToInt('f1'), tileToInt('c4')) 19 | ], 20 | [ 21 | // Sicilian Defense 22 | Move(tileToInt('e2'), tileToInt('e4')), 23 | Move(tileToInt('c7'), tileToInt('c5')), 24 | ], 25 | [ 26 | // French Defense 27 | Move(tileToInt('e2'), tileToInt('e4')), 28 | Move(tileToInt('e7'), tileToInt('e6')), 29 | ], 30 | [ 31 | // Caro-Kann Defense 32 | Move(tileToInt('e2'), tileToInt('e4')), 33 | Move(tileToInt('c7'), tileToInt('c6')), 34 | ], 35 | [ 36 | // Pirc Defense 37 | Move(tileToInt('e2'), tileToInt('e4')), 38 | Move(tileToInt('d7'), tileToInt('d6')), 39 | ], 40 | [ 41 | // Queen's Gambit 42 | Move(tileToInt('d2'), tileToInt('d4')), 43 | Move(tileToInt('d7'), tileToInt('d5')), 44 | Move(tileToInt('c2'), tileToInt('c4')), 45 | ], 46 | [ 47 | // Indian Defense 48 | Move(tileToInt('d2'), tileToInt('d4')), 49 | Move(tileToInt('g8'), tileToInt('f6')), 50 | ], 51 | [ 52 | // English Opening 53 | Move(tileToInt('c2'), tileToInt('c4')), 54 | ], 55 | [ 56 | // Reti Opening 57 | Move(tileToInt('g1'), tileToInt('f3')), 58 | ] 59 | ]; 60 | 61 | int tileToInt(String tile) { 62 | var file = tile.codeUnitAt(0) - 97; 63 | var rank = 8 - (int.tryParse(tile[1]) ?? 0); 64 | return rank * 8 + file; 65 | } 66 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls/moves_undo_redo_row/undo_redo_buttons.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/chess_view/game_info_and_controls/moves_undo_redo_row/rounded_icon_button.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | class UndoRedoButtons extends StatelessWidget { 6 | final AppModel appModel; 7 | 8 | bool get undoEnabled { 9 | if (appModel.playingWithAI) { 10 | return (appModel.game?.board.moveStack.length ?? 0) > 1 && 11 | !appModel.isAIsTurn; 12 | } else { 13 | return appModel.game?.board.moveStack.isNotEmpty ?? false; 14 | } 15 | } 16 | 17 | bool get redoEnabled { 18 | if (appModel.playingWithAI) { 19 | return (appModel.game?.board.redoStack.length ?? 0) > 1 && 20 | !appModel.isAIsTurn; 21 | } else { 22 | return appModel.game?.board.redoStack.isNotEmpty ?? false; 23 | } 24 | } 25 | 26 | UndoRedoButtons(this.appModel); 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Row( 31 | children: [ 32 | Expanded( 33 | child: RoundedIconButton( 34 | CupertinoIcons.arrow_counterclockwise, 35 | onPressed: undoEnabled ? () => undo() : null, 36 | ), 37 | ), 38 | SizedBox(width: 10), 39 | Expanded( 40 | child: RoundedIconButton( 41 | CupertinoIcons.arrow_clockwise, 42 | onPressed: redoEnabled ? () => redo() : null, 43 | ), 44 | ), 45 | ], 46 | ); 47 | } 48 | 49 | void undo() { 50 | if (appModel.playingWithAI) { 51 | appModel.game?.undoTwoMoves(); 52 | } else { 53 | appModel.game?.undoMove(); 54 | } 55 | } 56 | 57 | void redo() { 58 | if (appModel.playingWithAI) { 59 | appModel.game?.redoTwoMoves(); 60 | } else { 61 | appModel.game?.redoMove(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls/game_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 3 | import 'package:en_passant/views/components/shared/text_variable.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | class GameStatus extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Consumer( 11 | builder: (context, appModel, child) => Row( 12 | children: [ 13 | TextRegular(_getStatus(appModel)), 14 | !appModel.gameOver && appModel.playerCount == 1 && appModel.isAIsTurn 15 | ? CupertinoActivityIndicator(radius: 12) 16 | : Container() 17 | ], 18 | mainAxisAlignment: MainAxisAlignment.center, 19 | ), 20 | ); 21 | } 22 | 23 | String _getStatus(AppModel appModel) { 24 | if (!appModel.gameOver) { 25 | if (appModel.playerCount == 1) { 26 | if (appModel.isAIsTurn) { 27 | return 'AI [Level ${appModel.aiDifficulty}] is thinking '; 28 | } else { 29 | return 'Your turn'; 30 | } 31 | } else { 32 | if (appModel.turn == Player.player1) { 33 | return 'White\'s turn'; 34 | } else { 35 | return 'Black\'s turn'; 36 | } 37 | } 38 | } else { 39 | if (appModel.stalemate) { 40 | return 'Stalemate'; 41 | } else { 42 | if (appModel.playerCount == 1) { 43 | if (appModel.isAIsTurn) { 44 | return 'You Win!'; 45 | } else { 46 | return 'You Lose :('; 47 | } 48 | } else { 49 | if (appModel.turn == Player.player1) { 50 | return 'Black wins!'; 51 | } else { 52 | return 'White wins!'; 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/views/components/settings_view/piece_preview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:en_passant/logic/shared_functions.dart'; 4 | import 'package:en_passant/model/app_model.dart'; 5 | import 'package:flame/flame.dart'; 6 | import 'package:flame/game.dart'; 7 | import 'package:flame/sprite.dart'; 8 | import 'package:flutter/cupertino.dart'; 9 | 10 | class PiecePreview extends Game { 11 | AppModel appModel; 12 | 13 | Map get imageMap { 14 | return { 15 | 0: 'pieces/${formatPieceTheme(appModel.pieceTheme)}/king_black.png', 16 | 1: 'pieces/${formatPieceTheme(appModel.pieceTheme)}/queen_white.png', 17 | 2: 'pieces/${formatPieceTheme(appModel.pieceTheme)}/rook_white.png', 18 | 3: 'pieces/${formatPieceTheme(appModel.pieceTheme)}/bishop_black.png', 19 | 4: 'pieces/${formatPieceTheme(appModel.pieceTheme)}/knight_black.png', 20 | 5: 'pieces/${formatPieceTheme(appModel.pieceTheme)}/pawn_white.png', 21 | }; 22 | } 23 | 24 | Map spriteMap = Map(); 25 | bool rendered = false; 26 | 27 | PiecePreview(this.appModel) { 28 | loadSpriteImages(); 29 | } 30 | 31 | loadSpriteImages() async { 32 | for (var index = 0; index < 6; index++) { 33 | spriteMap[index] = Sprite(await Flame.images.load(imageMap[index] ?? "")); 34 | } 35 | } 36 | 37 | @override 38 | void render(Canvas canvas) { 39 | for (var index = 0; index < 6; index++) { 40 | canvas.drawRect( 41 | Rect.fromLTWH((index % 2) * 40.0, (index / 2).floor() * 40.0, 40, 40), 42 | Paint() 43 | ..color = (index + (index / 2).floor()) % 2 == 0 44 | ? appModel.theme.lightTile 45 | : appModel.theme.darkTile, 46 | ); 47 | spriteMap[index]?.render( 48 | canvas, 49 | size: Vector2(30, 30), 50 | position: Vector2( 51 | (index % 2) * 40.0 + 5, 52 | (index / 2).floor() * 40.0 + 5, 53 | ), 54 | ); 55 | } 56 | } 57 | 58 | @override 59 | void update(double t) {} 60 | } 61 | -------------------------------------------------------------------------------- /lib/views/components/main_menu_view/main_menu_buttons.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/shared/rounded_button.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:url_launcher/url_launcher.dart'; 5 | 6 | import '../../chess_view.dart'; 7 | import '../../settings_view.dart'; 8 | 9 | class MainMenuButtons extends StatelessWidget { 10 | final AppModel appModel; 11 | 12 | MainMenuButtons(this.appModel); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Container( 17 | width: double.infinity, 18 | child: Column( 19 | children: [ 20 | RoundedButton( 21 | 'Start', 22 | onPressed: () { 23 | Navigator.push( 24 | context, 25 | CupertinoPageRoute( 26 | builder: (context) { 27 | appModel.newGame(context, notify: false); 28 | return ChessView(appModel); 29 | }, 30 | ), 31 | ); 32 | }, 33 | ), 34 | SizedBox(height: 10), 35 | Row( 36 | children: [ 37 | Expanded( 38 | child: RoundedButton( 39 | 'GitHub', 40 | onPressed: () { 41 | _openGitHub(); 42 | }, 43 | ), 44 | ), 45 | SizedBox(width: 10), 46 | Expanded( 47 | child: RoundedButton( 48 | 'Settings', 49 | onPressed: () { 50 | Navigator.push( 51 | context, 52 | CupertinoPageRoute( 53 | builder: (context) => SettingsView(), 54 | ), 55 | ); 56 | }, 57 | ), 58 | ), 59 | ], 60 | ), 61 | ], 62 | ), 63 | ); 64 | } 65 | 66 | void _openGitHub() async { 67 | const url = 'https://github.com/PScottZero/EnPassant'; 68 | if (await canLaunch(url)) { 69 | await launch(url); 70 | } else { 71 | throw 'Could not launch $url'; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/views/chess_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:en_passant/model/app_model.dart'; 4 | import 'package:en_passant/views/components/chess_view/chess_board_widget.dart'; 5 | import 'package:en_passant/views/components/chess_view/game_info_and_controls.dart'; 6 | import 'package:en_passant/views/components/chess_view/promotion_dialog.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | import 'components/chess_view/game_info_and_controls/game_status.dart'; 11 | import 'components/shared/bottom_padding.dart'; 12 | 13 | class ChessView extends StatefulWidget { 14 | final AppModel appModel; 15 | 16 | ChessView(this.appModel); 17 | 18 | @override 19 | _ChessViewState createState() => _ChessViewState(appModel); 20 | } 21 | 22 | class _ChessViewState extends State { 23 | AppModel appModel; 24 | 25 | _ChessViewState(this.appModel); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Consumer( 30 | builder: (context, appModel, child) { 31 | if (appModel.promotionRequested) { 32 | appModel.promotionRequested = false; 33 | WidgetsBinding.instance 34 | .addPostFrameCallback((_) => _showPromotionDialog(appModel)); 35 | } 36 | return WillPopScope( 37 | onWillPop: _willPopCallback, 38 | child: Container( 39 | decoration: BoxDecoration(gradient: appModel.theme.background), 40 | padding: EdgeInsets.all(30), 41 | child: Column( 42 | children: [ 43 | Spacer(), 44 | ChessBoardWidget(appModel), 45 | SizedBox(height: 30), 46 | GameStatus(), 47 | Spacer(), 48 | GameInfoAndControls(appModel), 49 | BottomPadding(), 50 | ], 51 | ), 52 | ), 53 | ); 54 | }, 55 | ); 56 | } 57 | 58 | void _showPromotionDialog(AppModel appModel) { 59 | showCupertinoDialog( 60 | context: context, 61 | builder: (BuildContext context) { 62 | return PromotionDialog(appModel); 63 | }, 64 | ); 65 | } 66 | 67 | Future _willPopCallback() async { 68 | appModel.exitChessView(); 69 | return true; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/views/components/settings_view/piece_theme_picker.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/settings_view/piece_preview.dart'; 3 | import 'package:en_passant/views/components/shared/text_variable.dart'; 4 | import 'package:flame/game.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class PieceThemePicker extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return Consumer( 12 | builder: (context, appModel, child) => Column( 13 | children: [ 14 | Container( 15 | child: TextSmall('Piece Theme'), 16 | padding: EdgeInsets.all(10), 17 | ), 18 | ClipRRect( 19 | borderRadius: BorderRadius.all(Radius.circular(15)), 20 | child: Container( 21 | height: 120, 22 | decoration: BoxDecoration(color: Color(0x20000000)), 23 | child: Row( 24 | children: [ 25 | Expanded( 26 | child: CupertinoPicker( 27 | scrollController: FixedExtentScrollController( 28 | initialItem: appModel.pieceThemeIndex, 29 | ), 30 | selectionOverlay: CupertinoPickerDefaultSelectionOverlay( 31 | background: Color(0x20000000), 32 | ), 33 | itemExtent: 50, 34 | onSelectedItemChanged: appModel.setPieceTheme, 35 | children: appModel.pieceThemes 36 | .map( 37 | (theme) => Container( 38 | padding: EdgeInsets.all(10), 39 | child: TextRegular(theme), 40 | ), 41 | ) 42 | .toList(), 43 | ), 44 | ), 45 | Container( 46 | height: 120, 47 | width: 80, 48 | child: GameWidget( 49 | game: PiecePreview(appModel), 50 | ), 51 | ), 52 | ], 53 | ), 54 | ), 55 | ), 56 | ], 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/logic/move_calculation/ai_move_calculation.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:en_passant/logic/move_calculation/move_classes/move_and_value.dart'; 4 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 5 | 6 | import '../chess_board.dart'; 7 | import '../shared_functions.dart'; 8 | import 'move_calculation.dart'; 9 | import 'move_classes/move.dart'; 10 | 11 | const INITIAL_ALPHA = -40000; 12 | const STALEMATE_ALPHA = -20000; 13 | const INITIAL_BETA = 40000; 14 | const STALEMATE_BETA = 20000; 15 | 16 | Move calculateAIMove(Map args) { 17 | ChessBoard board = args['board']; 18 | if (board.possibleOpenings.isNotEmpty) { 19 | return _openingMove(board, args['aiPlayer']); 20 | } else { 21 | return _alphaBeta(board, args['aiPlayer'], Move(0, 0), 0, 22 | args['aiDifficulty'], INITIAL_ALPHA, INITIAL_BETA) 23 | .move; 24 | } 25 | } 26 | 27 | MoveAndValue _alphaBeta(ChessBoard board, Player player, Move move, int depth, 28 | int maxDepth, int alpha, int beta) { 29 | if (depth == maxDepth) { 30 | return MoveAndValue(move, boardValue(board)); 31 | } 32 | var bestMove = MoveAndValue( 33 | Move(0, 0), player == Player.player1 ? INITIAL_ALPHA : INITIAL_BETA); 34 | for (var move in allMoves(player, board, maxDepth)) { 35 | push(move, board, promotionType: move.promotionType); 36 | var result = _alphaBeta( 37 | board, oppositePlayer(player), move, depth + 1, maxDepth, alpha, beta); 38 | result.move = move; 39 | pop(board); 40 | if (player == Player.player1) { 41 | if (result.value > bestMove.value) { 42 | bestMove = result; 43 | } 44 | alpha = max(alpha, bestMove.value); 45 | if (alpha >= beta) { 46 | break; 47 | } 48 | } else { 49 | if (result.value < bestMove.value) { 50 | bestMove = result; 51 | } 52 | beta = min(beta, bestMove.value); 53 | if (beta <= alpha) { 54 | break; 55 | } 56 | } 57 | } 58 | if (bestMove.value.abs() == INITIAL_BETA && !kingInCheck(player, board)) { 59 | if (piecesForPlayer(player, board).length == 1) { 60 | bestMove.value = 61 | player == Player.player1 ? STALEMATE_BETA : STALEMATE_ALPHA; 62 | } else { 63 | bestMove.value = 64 | player == Player.player1 ? STALEMATE_ALPHA : STALEMATE_BETA; 65 | } 66 | } 67 | return bestMove; 68 | } 69 | 70 | Move _openingMove(ChessBoard board, Player aiPlayer) { 71 | List possibleMoves = board.possibleOpenings 72 | .map((opening) => opening[board.moveCount]) 73 | .toList(); 74 | return possibleMoves[Random.secure().nextInt(possibleMoves.length)]; 75 | } 76 | -------------------------------------------------------------------------------- /lib/logic/chess_piece_sprite.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/model/app_model.dart'; 2 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 3 | import 'package:flame/components.dart'; 4 | import 'package:flame/flame.dart'; 5 | import 'package:flame_audio/flame_audio.dart'; 6 | import 'package:flame/sprite.dart'; 7 | 8 | import 'chess_piece.dart'; 9 | import 'shared_functions.dart'; 10 | 11 | class ChessPieceSprite { 12 | ChessPieceType? type; 13 | String? pieceTheme; 14 | int? tile; 15 | Sprite? sprite; 16 | double? spriteX; 17 | double? spriteY; 18 | double offsetX = 0; 19 | double offsetY = 0; 20 | 21 | ChessPieceSprite(ChessPiece piece, String pieceTheme) { 22 | this.tile = piece.tile; 23 | this.type = piece.type; 24 | this.pieceTheme = pieceTheme; 25 | initSprite(piece); 26 | } 27 | 28 | void update(double tileSize, AppModel appModel, ChessPiece piece) { 29 | if (piece.type != this.type) { 30 | this.type = piece.type; 31 | initSprite(piece); 32 | } 33 | if (piece.tile != this.tile) { 34 | this.tile = piece.tile; 35 | offsetX = 0; 36 | offsetY = 0; 37 | } 38 | var destX = getXFromTile(tile ?? 0, tileSize, appModel); 39 | var destY = getYFromTile(tile ?? 0, tileSize, appModel); 40 | if ((destX - (spriteX ?? 0)).abs() <= 0.1) { 41 | spriteX = destX; 42 | offsetX = 0; 43 | } else { 44 | if (offsetX == 0) { 45 | offsetX = (destX - (spriteX ?? 0)) / 10; 46 | } 47 | if (spriteX != null) { 48 | spriteX = (spriteX ?? 0) + offsetX; 49 | } 50 | playSound(destX, destY, appModel); 51 | } 52 | if ((destY - (spriteY ?? 0)).abs() <= 0.1) { 53 | spriteY = destY; 54 | offsetY = 0; 55 | } else { 56 | if (offsetY == 0) { 57 | offsetY += (destY - (spriteY ?? 0)) / 10; 58 | } 59 | if (spriteX != null) { 60 | spriteY = (spriteY ?? 0) + offsetY; 61 | } 62 | playSound(destX, destY, appModel); 63 | } 64 | } 65 | 66 | void playSound(double destX, double destY, AppModel appModel) async { 67 | if ((destX - (spriteX ?? 0)).abs() <= 0.1 && 68 | (destY - (spriteY ?? 0)).abs() <= 0.1) { 69 | if (appModel.soundEnabled) { 70 | FlameAudio.play('audio/piece_moved.mp3'); 71 | } 72 | } 73 | } 74 | 75 | void initSprite(ChessPiece piece) async { 76 | String color = piece.player == Player.player1 ? 'white' : 'black'; 77 | String pieceName = pieceTypeToString(piece.type); 78 | if (piece.type == ChessPieceType.promotion) { 79 | pieceName = 'pawn'; 80 | } 81 | sprite = Sprite(await Flame.images.load( 82 | 'pieces/${formatPieceTheme(pieceTheme ?? "")}/${pieceName}_$color.png')); 83 | } 84 | 85 | void initSpritePosition(double tileSize, AppModel appModel) { 86 | spriteX = getXFromTile(tile ?? 0, tileSize, appModel); 87 | spriteY = getYFromTile(tile ?? 0, tileSize, appModel); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /lib/logic/move_calculation/piece_square_tables.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/logic/chess_piece.dart'; 2 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 3 | 4 | const KING_TABLE = [ 5 | -30, -40, -40, -50, -50, -40, -40, -30, 6 | -30, -40, -40, -50, -50, -40, -40, -30, 7 | -30, -40, -40, -50, -50, -40, -40, -30, 8 | -30, -40, -40, -50, -50, -40, -40, -30, 9 | -20, -30, -30, -40, -40, -30, -30, -20, 10 | -10, -20, -20, -20, -20, -20, -20, -10, 11 | 20, 20, 0, 0, 0, 0, 20, 20, 12 | 20, 30, 10, 0, 0, 10, 30, 20 13 | ]; 14 | 15 | const KING_ENDGAME_TABLE = [ 16 | -50, -40, -30, -20, -20, -30, -40, -50, 17 | -30, -20, -10, 0, 0, -10, -20, -30, 18 | -30, -10, 20, 30, 30, 20, -10, -30, 19 | -30, -10, 30, 40, 40, 30, -10, -30, 20 | -30, -10, 30, 40, 40, 30, -10, -30, 21 | -30, -10, 20, 30, 30, 20, -10, -30, 22 | -30, -30, 0, 0, 0, 0, -30, -30, 23 | -50, -30, -30, -30, -30, -30, -30, -50 24 | ]; 25 | 26 | const QUEEN_TABLE = [ 27 | -20, -10, -10, -5, -5, -10, -10, -20, 28 | -10, 0, 0, 0, 0, 0, 0, -10, 29 | -10, 0, 5, 5, 5, 5, 0, -10, 30 | -5, 0, 5, 5, 5, 5, 0, -5, 31 | 0, 0, 5, 5, 5, 5, 0, -5, 32 | -10, 5, 5, 5, 5, 5, 0, -10, 33 | -10, 0, 5, 0, 0, 0, 0, -10, 34 | -20, -10, -10, -5, -5, -10, -10, -20 35 | ]; 36 | 37 | const ROOK_TABLE = [ 38 | 0, 0, 0, 0, 0, 0, 0, 0, 39 | 5, 10, 10, 10, 10, 10, 10, 5, 40 | -5, 0, 0, 0, 0, 0, 0, -5, 41 | -5, 0, 0, 0, 0, 0, 0, -5, 42 | -5, 0, 0, 0, 0, 0, 0, -5, 43 | -5, 0, 0, 0, 0, 0, 0, -5, 44 | -5, 0, 0, 0, 0, 0, 0, -5, 45 | 0, 0, 0, 5, 5, 0, 0, 0 46 | ]; 47 | 48 | const BISHOP_TABLE = [ 49 | -20, -10, -10, -10, -10, -10, -10, -20, 50 | -10, 0, 0, 0, 0, 0, 0, -10, 51 | -10, 0, 5, 10, 10, 5, 0, -10, 52 | -10, 5, 5, 10, 10, 5, 5, -10, 53 | -10, 0, 10, 10, 10, 10, 0, -10, 54 | -10, 10, 10, 10, 10, 10, 10, -10, 55 | -10, 5, 0, 0, 0, 0, 5, -10, 56 | -20, -10, -10, -10, -10, -10, -10, -20 57 | ]; 58 | 59 | const KNIGHT_TABLE = [ 60 | -50, -40, -30, -30, -30, -30, -40, -50, 61 | -40, -20, 0, 0, 0, 0, -20, -40, 62 | -30, 0, 10, 15, 15, 10, 0, -30, 63 | -30, 5, 15, 20, 20, 15, 5, -30, 64 | -30, 0, 15, 20, 20, 15, 0, -30, 65 | -30, 5, 10, 15, 15, 10, 5, -30, 66 | -40, -20, 0, 5, 5, 0, -20, -40, 67 | -50, -40, -30, -30, -30, -30, -40, -50 68 | ]; 69 | 70 | const PAWN_TABLE = [ 71 | 0, 0, 0, 0, 0, 0, 0, 0, 72 | 50, 50, 50, 50, 50, 50, 50, 50, 73 | 10, 10, 20, 30, 30, 20, 10, 10, 74 | 5, 5, 10, 25, 25, 10, 5, 5, 75 | 0, 0, 0, 20, 20, 0, 0, 0, 76 | 5, -5, -10, 0, 0, -10, -5, 5, 77 | 5, 10, 10, -20, -20, 10, 10, 5, 78 | 0, 0, 0, 0, 0, 0, 0, 0 79 | ]; 80 | 81 | int squareValue(ChessPiece piece, bool inEndGame) { 82 | var tile = piece.player == Player.player1 ? 83 | piece.tile : piece.tile + 56 - 16 * (piece.tile / 8).floor(); 84 | int value; 85 | switch (piece.type) { 86 | case ChessPieceType.pawn: { value = PAWN_TABLE[tile]; } 87 | break; 88 | case ChessPieceType.knight: { value = KNIGHT_TABLE[tile]; } 89 | break; 90 | case ChessPieceType.bishop: { value = BISHOP_TABLE[tile]; } 91 | break; 92 | case ChessPieceType.rook: { value = ROOK_TABLE[tile]; } 93 | break; 94 | case ChessPieceType.queen: { value = QUEEN_TABLE[tile]; } 95 | break; 96 | case ChessPieceType.king: { 97 | value = inEndGame ? KING_ENDGAME_TABLE[tile] : KING_TABLE[tile]; 98 | } 99 | break; 100 | default: { value = 0; } 101 | break; 102 | } 103 | return piece.player == Player.player1 ? value : -value; 104 | } 105 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: en_passant 2 | description: A Flutter chess app. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.7.5+19 19 | 20 | environment: 21 | sdk: ">=3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | provider: ^6.0.5 27 | flame: ^1.7.3 28 | flame_audio: 29 | url_launcher: ^6.0.2 30 | shared_preferences: 31 | async: 32 | 33 | # The following adds the Cupertino Icons font to your application. 34 | # Use with the CupertinoIcons class for iOS style icons. 35 | cupertino_icons: ^1.0.0 36 | 37 | dev_dependencies: 38 | flutter_test: 39 | sdk: flutter 40 | flutter_launcher_icons: ^0.13.1 41 | 42 | flutter_icons: 43 | android: true 44 | ios: true 45 | image_path_android: "assets/icons/icon.png" 46 | image_path_ios: "assets/icons/icon.png" 47 | adaptive_icon_foreground: "assets/icons/icon_foreground.png" 48 | adaptive_icon_background: "assets/icons/icon_background.png" 49 | 50 | # For information on the generic Dart part of this file, see the 51 | # following page: https://dart.dev/tools/pub/pubspec 52 | 53 | # The following section is specific to Flutter. 54 | flutter: 55 | fonts: 56 | - family: Jura 57 | fonts: 58 | - asset: assets/font/Jura-VariableFont_wght.ttf 59 | 60 | # The following line ensures that the Material Icons font is 61 | # included with your application, so that you can use the icons in 62 | # the material Icons class. 63 | uses-material-design: true 64 | 65 | # To add assets to your application, add an assets section, like this: 66 | # assets: 67 | # - images/a_dot_burr.jpeg 68 | # - images/a_dot_ham.jpeg 69 | 70 | # An image asset can refer to one or more resolution-specific "variants", see 71 | # https://flutter.dev/assets-and-images/#resolution-aware. 72 | 73 | # For details regarding adding assets from package dependencies, see 74 | # https://flutter.dev/assets-and-images/#from-packages 75 | 76 | # To add custom fonts to your application, add a fonts section here, 77 | # in this "flutter" section. Each entry in this list should have a 78 | # "family" key with the font family name, and a "fonts" key with a 79 | # list giving the asset and other descriptors for the font. For 80 | # example: 81 | # fonts: 82 | # - family: Schyler 83 | # fonts: 84 | # - asset: fonts/Schyler-Regular.ttf 85 | # - asset: fonts/Schyler-Italic.ttf 86 | # style: italic 87 | # - family: Trajan Pro 88 | # fonts: 89 | # - asset: fonts/TrajanPro.ttf 90 | # - asset: fonts/TrajanPro_Bold.ttf 91 | # weight: 700 92 | # 93 | # For details regarding fonts from package dependencies, 94 | # see https://flutter.dev/custom-fonts/#from-packages 95 | assets: 96 | - assets/images/ 97 | - assets/images/pieces/classic/ 98 | - assets/images/pieces/letters/ 99 | - assets/images/pieces/angular/ 100 | - assets/images/pieces/8-bit/ 101 | - assets/images/pieces/videochess/ 102 | - assets/images/pieces/lewischessmen/ 103 | - assets/images/pieces/mexicocity/ 104 | - assets/audio/piece_moved.mp3 105 | -------------------------------------------------------------------------------- /lib/views/components/chess_view/game_info_and_controls/moves_undo_redo_row/move_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/logic/chess_piece.dart'; 2 | import 'package:en_passant/logic/move_calculation/move_classes/move_meta.dart'; 3 | import 'package:en_passant/logic/shared_functions.dart'; 4 | import 'package:en_passant/model/app_model.dart'; 5 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 6 | import 'package:en_passant/views/components/shared/text_variable.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | 9 | class MoveList extends StatelessWidget { 10 | final AppModel appModel; 11 | final ScrollController scrollController = ScrollController(); 12 | 13 | MoveList(this.appModel); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom()); 18 | return Container( 19 | height: 60, 20 | decoration: BoxDecoration( 21 | borderRadius: BorderRadius.all(Radius.circular(15)), 22 | color: Color(0x20000000), 23 | ), 24 | child: SingleChildScrollView( 25 | scrollDirection: Axis.horizontal, 26 | controller: scrollController, 27 | padding: EdgeInsets.only(left: 15, right: 15), 28 | child: Center(child: TextRegular(_allMoves())), 29 | ), 30 | ); 31 | } 32 | 33 | void _scrollToBottom() { 34 | if (appModel.moveListUpdated) { 35 | scrollController.jumpTo(scrollController.position.maxScrollExtent); 36 | appModel.moveListUpdated = false; 37 | } 38 | } 39 | 40 | String _allMoves() { 41 | var moveString = ''; 42 | appModel.moveMetaList.asMap().forEach((index, move) { 43 | var turnNumber = ((index + 1) / 2).ceil(); 44 | if (index % 2 == 0) { 45 | moveString += index == 0 ? '$turnNumber. ' : ' $turnNumber. '; 46 | } 47 | moveString += _moveToString(move); 48 | if (index % 2 == 0) { 49 | moveString += ' '; 50 | } 51 | }); 52 | if (appModel.gameOver) { 53 | if (appModel.turn == Player.player1) { 54 | moveString += ' '; 55 | } 56 | if (appModel.stalemate) { 57 | moveString += ' ½-½'; 58 | } else { 59 | moveString += appModel.turn == Player.player2 ? ' 1-0' : ' 0-1'; 60 | } 61 | } 62 | return moveString; 63 | } 64 | 65 | String _moveToString(MoveMeta meta) { 66 | String move; 67 | if (meta.kingCastle) { 68 | move = 'O-O'; 69 | } else if (meta.queenCastle) { 70 | move = 'O-O-O'; 71 | } else { 72 | String ambiguity = meta.rowIsAmbiguous 73 | ? '${_colToChar(tileToCol(meta.move?.from ?? 0))}' 74 | : ''; 75 | ambiguity += 76 | meta.colIsAmbiguous ? '${8 - tileToRow(meta.move?.from ?? 0)}' : ''; 77 | String takeString = meta.took ? 'x' : ''; 78 | String promotion = meta.promotion 79 | ? '=${_pieceToChar(meta.promotionType ?? ChessPieceType.promotion)}' 80 | : ''; 81 | String row = '${8 - tileToRow(meta.move?.to ?? 0)}'; 82 | String col = '${_colToChar(tileToCol(meta.move?.to ?? 0))}'; 83 | move = 84 | '${_pieceToChar(meta.type ?? ChessPieceType.promotion)}$ambiguity$takeString' + 85 | '$col$row$promotion'; 86 | } 87 | String check = meta.isCheck ? '+' : ''; 88 | String checkmate = meta.isCheckmate && !meta.isStalemate ? '#' : ''; 89 | return move + '$check$checkmate'; 90 | } 91 | 92 | String _pieceToChar(ChessPieceType type) { 93 | switch (type) { 94 | case ChessPieceType.king: 95 | { 96 | return 'K'; 97 | } 98 | case ChessPieceType.queen: 99 | { 100 | return 'Q'; 101 | } 102 | case ChessPieceType.rook: 103 | { 104 | return 'R'; 105 | } 106 | case ChessPieceType.bishop: 107 | { 108 | return 'B'; 109 | } 110 | case ChessPieceType.knight: 111 | { 112 | return 'N'; 113 | } 114 | case ChessPieceType.pawn: 115 | { 116 | return ''; 117 | } 118 | default: 119 | { 120 | return '?'; 121 | } 122 | } 123 | } 124 | 125 | String _colToChar(int col) { 126 | return String.fromCharCode(97 + col); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "40.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "60.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "29.png", 17 | "idiom" : "iphone", 18 | "scale" : "1x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "58.png", 23 | "idiom" : "iphone", 24 | "scale" : "2x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "87.png", 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "29x29" 32 | }, 33 | { 34 | "filename" : "80.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "120.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "40x40" 44 | }, 45 | { 46 | "filename" : "57.png", 47 | "idiom" : "iphone", 48 | "scale" : "1x", 49 | "size" : "57x57" 50 | }, 51 | { 52 | "filename" : "114.png", 53 | "idiom" : "iphone", 54 | "scale" : "2x", 55 | "size" : "57x57" 56 | }, 57 | { 58 | "filename" : "120.png", 59 | "idiom" : "iphone", 60 | "scale" : "2x", 61 | "size" : "60x60" 62 | }, 63 | { 64 | "filename" : "180.png", 65 | "idiom" : "iphone", 66 | "scale" : "3x", 67 | "size" : "60x60" 68 | }, 69 | { 70 | "filename" : "20.png", 71 | "idiom" : "ipad", 72 | "scale" : "1x", 73 | "size" : "20x20" 74 | }, 75 | { 76 | "filename" : "40.png", 77 | "idiom" : "ipad", 78 | "scale" : "2x", 79 | "size" : "20x20" 80 | }, 81 | { 82 | "filename" : "29.png", 83 | "idiom" : "ipad", 84 | "scale" : "1x", 85 | "size" : "29x29" 86 | }, 87 | { 88 | "filename" : "58.png", 89 | "idiom" : "ipad", 90 | "scale" : "2x", 91 | "size" : "29x29" 92 | }, 93 | { 94 | "filename" : "40.png", 95 | "idiom" : "ipad", 96 | "scale" : "1x", 97 | "size" : "40x40" 98 | }, 99 | { 100 | "filename" : "80.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "40x40" 104 | }, 105 | { 106 | "filename" : "50.png", 107 | "idiom" : "ipad", 108 | "scale" : "1x", 109 | "size" : "50x50" 110 | }, 111 | { 112 | "filename" : "100.png", 113 | "idiom" : "ipad", 114 | "scale" : "2x", 115 | "size" : "50x50" 116 | }, 117 | { 118 | "filename" : "72.png", 119 | "idiom" : "ipad", 120 | "scale" : "1x", 121 | "size" : "72x72" 122 | }, 123 | { 124 | "filename" : "144.png", 125 | "idiom" : "ipad", 126 | "scale" : "2x", 127 | "size" : "72x72" 128 | }, 129 | { 130 | "filename" : "76.png", 131 | "idiom" : "ipad", 132 | "scale" : "1x", 133 | "size" : "76x76" 134 | }, 135 | { 136 | "filename" : "152.png", 137 | "idiom" : "ipad", 138 | "scale" : "2x", 139 | "size" : "76x76" 140 | }, 141 | { 142 | "filename" : "167.png", 143 | "idiom" : "ipad", 144 | "scale" : "2x", 145 | "size" : "83.5x83.5" 146 | }, 147 | { 148 | "filename" : "1024.png", 149 | "idiom" : "ios-marketing", 150 | "scale" : "1x", 151 | "size" : "1024x1024" 152 | }, 153 | { 154 | "filename" : "48.png", 155 | "idiom" : "watch", 156 | "role" : "notificationCenter", 157 | "scale" : "2x", 158 | "size" : "24x24", 159 | "subtype" : "38mm" 160 | }, 161 | { 162 | "filename" : "55.png", 163 | "idiom" : "watch", 164 | "role" : "notificationCenter", 165 | "scale" : "2x", 166 | "size" : "27.5x27.5", 167 | "subtype" : "42mm" 168 | }, 169 | { 170 | "filename" : "58.png", 171 | "idiom" : "watch", 172 | "role" : "companionSettings", 173 | "scale" : "2x", 174 | "size" : "29x29" 175 | }, 176 | { 177 | "filename" : "87.png", 178 | "idiom" : "watch", 179 | "role" : "companionSettings", 180 | "scale" : "3x", 181 | "size" : "29x29" 182 | }, 183 | { 184 | "filename" : "80.png", 185 | "idiom" : "watch", 186 | "role" : "appLauncher", 187 | "scale" : "2x", 188 | "size" : "40x40", 189 | "subtype" : "38mm" 190 | }, 191 | { 192 | "filename" : "88.png", 193 | "idiom" : "watch", 194 | "role" : "appLauncher", 195 | "scale" : "2x", 196 | "size" : "44x44", 197 | "subtype" : "40mm" 198 | }, 199 | { 200 | "filename" : "100.png", 201 | "idiom" : "watch", 202 | "role" : "appLauncher", 203 | "scale" : "2x", 204 | "size" : "50x50", 205 | "subtype" : "44mm" 206 | }, 207 | { 208 | "filename" : "172.png", 209 | "idiom" : "watch", 210 | "role" : "quickLook", 211 | "scale" : "2x", 212 | "size" : "86x86", 213 | "subtype" : "38mm" 214 | }, 215 | { 216 | "filename" : "196.png", 217 | "idiom" : "watch", 218 | "role" : "quickLook", 219 | "scale" : "2x", 220 | "size" : "98x98", 221 | "subtype" : "42mm" 222 | }, 223 | { 224 | "filename" : "216.png", 225 | "idiom" : "watch", 226 | "role" : "quickLook", 227 | "scale" : "2x", 228 | "size" : "108x108", 229 | "subtype" : "44mm" 230 | }, 231 | { 232 | "filename" : "1024.png", 233 | "idiom" : "watch-marketing", 234 | "scale" : "1x", 235 | "size" : "1024x1024" 236 | }, 237 | { 238 | "filename" : "16.png", 239 | "idiom" : "mac", 240 | "scale" : "1x", 241 | "size" : "16x16" 242 | }, 243 | { 244 | "filename" : "32.png", 245 | "idiom" : "mac", 246 | "scale" : "2x", 247 | "size" : "16x16" 248 | }, 249 | { 250 | "filename" : "32.png", 251 | "idiom" : "mac", 252 | "scale" : "1x", 253 | "size" : "32x32" 254 | }, 255 | { 256 | "filename" : "64.png", 257 | "idiom" : "mac", 258 | "scale" : "2x", 259 | "size" : "32x32" 260 | }, 261 | { 262 | "filename" : "128.png", 263 | "idiom" : "mac", 264 | "scale" : "1x", 265 | "size" : "128x128" 266 | }, 267 | { 268 | "filename" : "256.png", 269 | "idiom" : "mac", 270 | "scale" : "2x", 271 | "size" : "128x128" 272 | }, 273 | { 274 | "filename" : "256.png", 275 | "idiom" : "mac", 276 | "scale" : "1x", 277 | "size" : "256x256" 278 | }, 279 | { 280 | "filename" : "512.png", 281 | "idiom" : "mac", 282 | "scale" : "2x", 283 | "size" : "256x256" 284 | }, 285 | { 286 | "filename" : "512.png", 287 | "idiom" : "mac", 288 | "scale" : "1x", 289 | "size" : "512x512" 290 | }, 291 | { 292 | "filename" : "1024.png", 293 | "idiom" : "mac", 294 | "scale" : "2x", 295 | "size" : "512x512" 296 | } 297 | ], 298 | "info" : { 299 | "author" : "xcode", 300 | "version" : 1 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /lib/model/app_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:math'; 3 | 4 | import 'package:en_passant/logic/chess_game.dart'; 5 | import 'package:en_passant/logic/move_calculation/move_classes/move_meta.dart'; 6 | import 'package:en_passant/logic/shared_functions.dart'; 7 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:shared_preferences/shared_preferences.dart'; 10 | 11 | import 'app_themes.dart'; 12 | 13 | const TIMER_ACCURACY_MS = 100; 14 | const PIECE_THEMES = [ 15 | 'Classic', 16 | 'Angular', 17 | '8-Bit', 18 | 'Letters', 19 | 'Video Chess', 20 | 'Lewis Chessmen', 21 | 'Mexico City' 22 | ]; 23 | 24 | class AppModel extends ChangeNotifier { 25 | int playerCount = 1; 26 | int aiDifficulty = 3; 27 | Player selectedSide = Player.player1; 28 | Player playerSide = Player.player1; 29 | int timeLimit = 0; 30 | String pieceTheme = 'Classic'; 31 | String themeName = 'Green'; 32 | bool showMoveHistory = true; 33 | bool allowUndoRedo = true; 34 | bool soundEnabled = true; 35 | bool showHints = true; 36 | bool flip = true; 37 | 38 | ChessGame? game; 39 | Timer? timer; 40 | bool gameOver = false; 41 | bool stalemate = false; 42 | bool promotionRequested = false; 43 | bool moveListUpdated = false; 44 | Player turn = Player.player1; 45 | List moveMetaList = []; 46 | Duration player1TimeLeft = Duration.zero; 47 | Duration player2TimeLeft = Duration.zero; 48 | 49 | List get pieceThemes { 50 | var pieceThemes = List.from(PIECE_THEMES); 51 | pieceThemes.sort(); 52 | return pieceThemes; 53 | } 54 | 55 | AppTheme get theme { 56 | return themeList[themeIndex]; 57 | } 58 | 59 | int get themeIndex { 60 | var themeIndex = 0; 61 | themeList.asMap().forEach((index, theme) { 62 | if (theme.name == themeName) { 63 | themeIndex = index; 64 | } 65 | }); 66 | return themeIndex; 67 | } 68 | 69 | int get pieceThemeIndex { 70 | var pieceThemeIndex = 0; 71 | pieceThemes.asMap().forEach((index, theme) { 72 | if (theme == pieceTheme) { 73 | pieceThemeIndex = index; 74 | } 75 | }); 76 | return pieceThemeIndex; 77 | } 78 | 79 | Player get aiTurn { 80 | return oppositePlayer(playerSide); 81 | } 82 | 83 | bool get isAIsTurn { 84 | return playingWithAI && (turn == aiTurn); 85 | } 86 | 87 | bool get playingWithAI { 88 | return playerCount == 1; 89 | } 90 | 91 | AppModel() { 92 | loadSharedPrefs(); 93 | } 94 | 95 | void newGame(BuildContext context, {bool notify = true}) { 96 | game?.cancelAIMove(); 97 | timer?.cancel(); 98 | gameOver = false; 99 | stalemate = false; 100 | turn = Player.player1; 101 | moveMetaList = []; 102 | player1TimeLeft = Duration(minutes: timeLimit); 103 | player2TimeLeft = Duration(minutes: timeLimit); 104 | if (selectedSide == Player.random) { 105 | playerSide = 106 | Random.secure().nextInt(2) == 0 ? Player.player1 : Player.player2; 107 | } 108 | game = ChessGame(this, context); 109 | timer = Timer.periodic(Duration(milliseconds: TIMER_ACCURACY_MS), (timer) { 110 | turn == Player.player1 111 | ? decrementPlayer1Timer() 112 | : decrementPlayer2Timer(); 113 | if ((player1TimeLeft == Duration.zero || 114 | player2TimeLeft == Duration.zero) && 115 | timeLimit != 0) { 116 | endGame(); 117 | } 118 | }); 119 | if (notify) { 120 | notifyListeners(); 121 | } 122 | } 123 | 124 | void exitChessView() { 125 | game?.cancelAIMove(); 126 | timer?.cancel(); 127 | notifyListeners(); 128 | } 129 | 130 | void pushMoveMeta(MoveMeta meta) { 131 | moveMetaList.add(meta); 132 | moveListUpdated = true; 133 | notifyListeners(); 134 | } 135 | 136 | void popMoveMeta() { 137 | moveMetaList.removeLast(); 138 | moveListUpdated = true; 139 | notifyListeners(); 140 | } 141 | 142 | void endGame() { 143 | gameOver = true; 144 | notifyListeners(); 145 | } 146 | 147 | void undoEndGame() { 148 | gameOver = false; 149 | notifyListeners(); 150 | } 151 | 152 | void changeTurn() { 153 | turn = oppositePlayer(turn); 154 | notifyListeners(); 155 | } 156 | 157 | void requestPromotion() { 158 | promotionRequested = true; 159 | notifyListeners(); 160 | } 161 | 162 | void setPlayerCount(int? count) { 163 | if (count != null) { 164 | playerCount = count; 165 | notifyListeners(); 166 | } 167 | } 168 | 169 | void setAIDifficulty(int? difficulty) { 170 | if (difficulty != null) { 171 | aiDifficulty = difficulty; 172 | notifyListeners(); 173 | } 174 | } 175 | 176 | void setPlayerSide(Player? side) { 177 | if (side != null) { 178 | selectedSide = side; 179 | if (side != Player.random) { 180 | playerSide = side; 181 | } 182 | notifyListeners(); 183 | } 184 | } 185 | 186 | void setTimeLimit(int? duration) { 187 | if (duration != null) { 188 | timeLimit = duration; 189 | player1TimeLeft = Duration(minutes: timeLimit); 190 | player2TimeLeft = Duration(minutes: timeLimit); 191 | notifyListeners(); 192 | } 193 | } 194 | 195 | void decrementPlayer1Timer() { 196 | if (player1TimeLeft.inMilliseconds > 0 && !gameOver) { 197 | player1TimeLeft = Duration( 198 | milliseconds: player1TimeLeft.inMilliseconds - TIMER_ACCURACY_MS); 199 | notifyListeners(); 200 | } 201 | } 202 | 203 | void decrementPlayer2Timer() { 204 | if (player2TimeLeft.inMilliseconds > 0 && !gameOver) { 205 | player2TimeLeft = Duration( 206 | milliseconds: player2TimeLeft.inMilliseconds - TIMER_ACCURACY_MS); 207 | notifyListeners(); 208 | } 209 | } 210 | 211 | void setTheme(int index) async { 212 | themeName = themeList[index].name ?? ""; 213 | final prefs = await SharedPreferences.getInstance(); 214 | prefs.setString('themeName', themeName); 215 | notifyListeners(); 216 | } 217 | 218 | void setPieceTheme(int index) async { 219 | pieceTheme = pieceThemes[index]; 220 | final prefs = await SharedPreferences.getInstance(); 221 | prefs.setString('pieceTheme', pieceTheme); 222 | notifyListeners(); 223 | } 224 | 225 | void setShowMoveHistory(bool show) async { 226 | final prefs = await SharedPreferences.getInstance(); 227 | showMoveHistory = show; 228 | prefs.setBool('showMoveHistory', show); 229 | notifyListeners(); 230 | } 231 | 232 | void setSoundEnabled(bool enabled) async { 233 | final prefs = await SharedPreferences.getInstance(); 234 | soundEnabled = enabled; 235 | prefs.setBool('soundEnabled', enabled); 236 | notifyListeners(); 237 | } 238 | 239 | void setShowHints(bool show) async { 240 | final prefs = await SharedPreferences.getInstance(); 241 | showHints = show; 242 | prefs.setBool('showHints', show); 243 | notifyListeners(); 244 | } 245 | 246 | void setFlipBoard(bool flip) async { 247 | final prefs = await SharedPreferences.getInstance(); 248 | this.flip = flip; 249 | prefs.setBool('flip', flip); 250 | notifyListeners(); 251 | } 252 | 253 | void setAllowUndoRedo(bool allow) async { 254 | final prefs = await SharedPreferences.getInstance(); 255 | this.allowUndoRedo = allow; 256 | prefs.setBool('allowUndoRedo', allow); 257 | notifyListeners(); 258 | } 259 | 260 | void loadSharedPrefs() async { 261 | final prefs = await SharedPreferences.getInstance(); 262 | themeName = prefs.getString('themeName') ?? 'Green'; 263 | pieceTheme = prefs.getString('pieceTheme') ?? 'Classic'; 264 | showMoveHistory = prefs.getBool('showMoveHistory') ?? true; 265 | soundEnabled = prefs.getBool('soundEnabled') ?? true; 266 | showHints = prefs.getBool('showHints') ?? true; 267 | flip = prefs.getBool('flip') ?? true; 268 | allowUndoRedo = prefs.getBool('allowUndoRedo') ?? true; 269 | notifyListeners(); 270 | } 271 | 272 | void update() { 273 | notifyListeners(); 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /lib/logic/move_calculation/move_calculation.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/logic/move_calculation/move_classes/direction.dart'; 2 | import 'package:en_passant/logic/move_calculation/move_classes/move_and_value.dart'; 3 | import 'package:en_passant/logic/shared_functions.dart'; 4 | import 'package:en_passant/views/components/chess_view/promotion_dialog.dart'; 5 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 6 | 7 | import '../chess_board.dart'; 8 | import '../chess_piece.dart'; 9 | import 'move_classes/move.dart'; 10 | 11 | const PAWN_DIAGONALS_1 = [DOWN_LEFT, DOWN_RIGHT]; 12 | const PAWN_DIAGONALS_2 = [UP_LEFT, UP_RIGHT]; 13 | const KNIGHT_MOVES = [ 14 | Direction(1, 2), 15 | Direction(-1, 2), 16 | Direction(1, -2), 17 | Direction(-1, -2), 18 | Direction(2, 1), 19 | Direction(-2, 1), 20 | Direction(2, -1), 21 | Direction(-2, -1) 22 | ]; 23 | const BISHOP_MOVES = [UP_RIGHT, DOWN_RIGHT, DOWN_LEFT, UP_LEFT]; 24 | const ROOK_MOVES = [UP, RIGHT, DOWN, LEFT]; 25 | const KING_QUEEN_MOVES = [ 26 | UP, 27 | UP_RIGHT, 28 | RIGHT, 29 | DOWN_RIGHT, 30 | DOWN, 31 | DOWN_LEFT, 32 | LEFT, 33 | UP_LEFT 34 | ]; 35 | 36 | List allMoves(Player player, ChessBoard board, int aiDifficulty) { 37 | List moves = []; 38 | var pieces = List.from(piecesForPlayer(player, board)); 39 | for (var piece in pieces) { 40 | var tiles = movesForPiece(piece, board); 41 | for (var tile in tiles) { 42 | if (piece.type == ChessPieceType.pawn && 43 | (tileToRow(tile) == 0 || tileToRow(tile) == 7)) { 44 | for (var promotion in PROMOTIONS) { 45 | var move = 46 | MoveAndValue(Move(piece.tile, tile, promotionType: promotion), 0); 47 | push(move.move, board, promotionType: promotion); 48 | move.value = boardValue(board); 49 | pop(board); 50 | moves.add(move); 51 | } 52 | } else { 53 | var move = MoveAndValue(Move(piece.tile, tile), 0); 54 | push(move.move, board); 55 | move.value = boardValue(board); 56 | pop(board); 57 | moves.add(move); 58 | } 59 | } 60 | } 61 | moves.sort((a, b) => _compareMoves(a, b, player, board)); 62 | return moves.map((move) => move.move).toList(); 63 | } 64 | 65 | int _compareMoves( 66 | MoveAndValue a, MoveAndValue b, Player player, ChessBoard board) { 67 | return player == Player.player1 68 | ? b.value.compareTo(a.value) 69 | : a.value.compareTo(b.value); 70 | } 71 | 72 | List movesForPiece(ChessPiece piece, ChessBoard board, 73 | {bool legal = true}) { 74 | List moves; 75 | switch (piece.type) { 76 | case ChessPieceType.pawn: 77 | { 78 | moves = _pawnMoves(piece, board); 79 | } 80 | break; 81 | case ChessPieceType.knight: 82 | { 83 | moves = _knightMoves(piece, board); 84 | } 85 | break; 86 | case ChessPieceType.bishop: 87 | { 88 | moves = _bishopMoves(piece, board); 89 | } 90 | break; 91 | case ChessPieceType.rook: 92 | { 93 | moves = _rookMoves(piece, board, legal); 94 | } 95 | break; 96 | case ChessPieceType.queen: 97 | { 98 | moves = _queenMoves(piece, board); 99 | } 100 | break; 101 | case ChessPieceType.king: 102 | { 103 | moves = _kingMoves(piece, board, legal); 104 | } 105 | break; 106 | default: 107 | { 108 | moves = []; 109 | } 110 | } 111 | if (legal) { 112 | moves.removeWhere((move) => _movePutsKingInCheck(piece, move, board)); 113 | } 114 | return moves; 115 | } 116 | 117 | List _pawnMoves(ChessPiece pawn, ChessBoard board) { 118 | List moves = []; 119 | var offset = pawn.player == Player.player1 ? -8 : 8; 120 | var firstTile = pawn.tile + offset; 121 | if (board.tiles[firstTile] == null) { 122 | moves.add(firstTile); 123 | if (pawn.moveCount == 0) { 124 | var secondTile = firstTile + offset; 125 | if (board.tiles[secondTile] == null) { 126 | moves.add(secondTile); 127 | } 128 | } 129 | } 130 | return moves + _pawnDiagonalAttacks(pawn, board); 131 | } 132 | 133 | List _pawnDiagonalAttacks(ChessPiece pawn, ChessBoard board) { 134 | List moves = []; 135 | var diagonals = 136 | pawn.player == Player.player1 ? PAWN_DIAGONALS_1 : PAWN_DIAGONALS_2; 137 | for (var diagonal in diagonals) { 138 | var row = tileToRow(pawn.tile) + diagonal.up; 139 | var col = tileToCol(pawn.tile) + diagonal.right; 140 | if (_inBounds(row, col)) { 141 | var takenPiece = board.tiles[_rowColToTile(row, col)]; 142 | if ((takenPiece != null && 143 | takenPiece.player == oppositePlayer(pawn.player)) || 144 | _canTakeEnPassant(pawn.player, _rowColToTile(row, col), board)) { 145 | moves.add(_rowColToTile(row, col)); 146 | } 147 | } 148 | } 149 | return moves; 150 | } 151 | 152 | bool _canTakeEnPassant(Player pawnPlayer, int diagonal, ChessBoard board) { 153 | var offset = (pawnPlayer == Player.player1) ? 8 : -8; 154 | var takenPiece = board.tiles[diagonal + offset]; 155 | return takenPiece != null && 156 | takenPiece.player != pawnPlayer && 157 | takenPiece == board.enPassantPiece; 158 | } 159 | 160 | List _knightMoves(ChessPiece knight, ChessBoard board) { 161 | return _movesFromDirections(knight, board, KNIGHT_MOVES, false); 162 | } 163 | 164 | List _bishopMoves(ChessPiece bishop, ChessBoard board) { 165 | return _movesFromDirections(bishop, board, BISHOP_MOVES, true); 166 | } 167 | 168 | List _rookMoves(ChessPiece rook, ChessBoard board, bool legal) { 169 | return _movesFromDirections(rook, board, ROOK_MOVES, true) + 170 | _rookCastleMove(rook, board, legal); 171 | } 172 | 173 | List _queenMoves(ChessPiece queen, ChessBoard board) { 174 | return _movesFromDirections(queen, board, KING_QUEEN_MOVES, true); 175 | } 176 | 177 | List _kingMoves(ChessPiece king, ChessBoard board, bool legal) { 178 | return _movesFromDirections(king, board, KING_QUEEN_MOVES, false) + 179 | _kingCastleMoves(king, board, legal); 180 | } 181 | 182 | List _rookCastleMove(ChessPiece rook, ChessBoard board, bool legal) { 183 | if (!legal || !kingInCheck(rook.player, board)) { 184 | var king = kingForPlayer(rook.player, board); 185 | if (_canCastle(king, rook, board, legal)) { 186 | return [king?.tile ?? 0]; 187 | } 188 | } 189 | return []; 190 | } 191 | 192 | List _kingCastleMoves(ChessPiece king, ChessBoard board, bool legal) { 193 | List moves = []; 194 | if (!legal || !kingInCheck(king.player, board)) { 195 | for (var rook in rooksForPlayer(king.player, board)) { 196 | if (_canCastle(king, rook, board, legal)) { 197 | moves.add(rook.tile); 198 | } 199 | } 200 | } 201 | return moves; 202 | } 203 | 204 | bool _canCastle( 205 | ChessPiece? king, ChessPiece rook, ChessBoard board, bool legal) { 206 | if (rook.moveCount == 0 && king?.moveCount == 0) { 207 | var offset = (king?.tile ?? 0) - rook.tile > 0 ? 1 : -1; 208 | var tile = rook.tile; 209 | while (tile != king?.tile) { 210 | tile += offset; 211 | if ((board.tiles[tile] != null && tile != king?.tile) || 212 | (legal && 213 | _kingInCheckAtTile( 214 | tile, king?.player ?? Player.player1, board))) { 215 | return false; 216 | } 217 | } 218 | return true; 219 | } 220 | return false; 221 | } 222 | 223 | List _movesFromDirections(ChessPiece piece, ChessBoard board, 224 | List directions, bool repeat) { 225 | List moves = []; 226 | for (var direction in directions) { 227 | var row = tileToRow(piece.tile); 228 | var col = tileToCol(piece.tile); 229 | do { 230 | row += direction.up; 231 | col += direction.right; 232 | if (_inBounds(row, col)) { 233 | var possiblePiece = board.tiles[_rowColToTile(row, col)]; 234 | if (possiblePiece != null) { 235 | if (possiblePiece.player != piece.player) { 236 | moves.add(_rowColToTile(row, col)); 237 | } 238 | break; 239 | } else { 240 | moves.add(_rowColToTile(row, col)); 241 | } 242 | } 243 | if (!repeat) { 244 | break; 245 | } 246 | } while (_inBounds(row, col)); 247 | } 248 | return moves; 249 | } 250 | 251 | bool _movePutsKingInCheck(ChessPiece piece, int move, ChessBoard board) { 252 | push(Move(piece.tile, move), board); 253 | var check = kingInCheck(piece.player, board); 254 | pop(board); 255 | return check; 256 | } 257 | 258 | bool _kingInCheckAtTile(int tile, Player player, ChessBoard board) { 259 | for (var piece in piecesForPlayer(oppositePlayer(player), board)) { 260 | if (movesForPiece(piece, board, legal: false).contains(tile)) { 261 | return true; 262 | } 263 | } 264 | return false; 265 | } 266 | 267 | bool kingInCheck(Player player, ChessBoard board) { 268 | for (var piece in piecesForPlayer(oppositePlayer(player), board)) { 269 | if (movesForPiece(piece, board, legal: false) 270 | .contains(kingForPlayer(player, board)?.tile)) { 271 | return true; 272 | } 273 | } 274 | return false; 275 | } 276 | 277 | bool kingInCheckmate(Player player, ChessBoard board) { 278 | for (var piece in piecesForPlayer(player, board)) { 279 | if (movesForPiece(piece, board).isNotEmpty) { 280 | return false; 281 | } 282 | } 283 | return true; 284 | } 285 | 286 | bool _inBounds(int row, int col) { 287 | return row >= 0 && row < 8 && col >= 0 && col < 8; 288 | } 289 | 290 | int _rowColToTile(int row, int col) { 291 | return row * 8 + col; 292 | } 293 | -------------------------------------------------------------------------------- /lib/model/app_themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppTheme { 4 | String? name; 5 | LinearGradient? background; 6 | Color lightTile; 7 | Color darkTile; 8 | Color moveHint; 9 | Color checkHint; 10 | Color latestMove; 11 | Color border; 12 | 13 | AppTheme({ 14 | this.name, 15 | this.background, 16 | this.lightTile = const Color(0xFFC9B28F), 17 | this.darkTile = const Color(0xFF69493b), 18 | this.moveHint = const Color(0xdd5c81c4), 19 | this.latestMove = const Color(0xccc47937), 20 | this.checkHint = const Color(0x88ff0000), 21 | this.border = const Color(0xffffffff), 22 | }); 23 | } 24 | 25 | List get themeList { 26 | var themeList = [ 27 | AppTheme( 28 | name: 'Bismuth', 29 | background: LinearGradient( 30 | begin: Alignment.topCenter, 31 | end: Alignment.bottomCenter, 32 | colors: [ 33 | Color(0xff2560a5), 34 | Color(0xff2f6e72), 35 | Color(0xff4faa55), 36 | Color(0xffe6de50), 37 | Color(0xffdb70eb), 38 | ], 39 | ), 40 | lightTile: Color(0xff4faa55), 41 | darkTile: Color(0xff2560a5), 42 | moveHint: Color(0xaaffff00), 43 | latestMove: Color(0xaadb70eb), 44 | border: Color(0xff184387), 45 | ), 46 | AppTheme( 47 | name: 'Candy', 48 | background: LinearGradient( 49 | begin: Alignment.topCenter, 50 | end: Alignment.bottomCenter, 51 | colors: [ 52 | Color(0xff8934eb), 53 | Color(0xff004f80), 54 | ], 55 | ), 56 | lightTile: Color(0xff0088ff), 57 | darkTile: Color(0xff8934eb), 58 | moveHint: Color(0xdddb70eb), 59 | checkHint: Color(0x88ff0000), 60 | latestMove: Color(0xcc2dba6f), 61 | border: Color(0xffdb70eb), 62 | ), 63 | AppTheme( 64 | name: 'Blue', 65 | background: LinearGradient( 66 | begin: Alignment.topCenter, 67 | end: Alignment.bottomCenter, 68 | colors: [ 69 | Color(0xff236e91), 70 | Color(0xff0f4964), 71 | ], 72 | ), 73 | ), 74 | AppTheme( 75 | name: 'Green', 76 | background: LinearGradient( 77 | begin: Alignment.topCenter, 78 | end: Alignment.bottomCenter, 79 | colors: [ 80 | Color(0xff25804f), 81 | Color(0xff00584f), 82 | ], 83 | ), 84 | ), 85 | AppTheme( 86 | name: 'Red', 87 | background: LinearGradient( 88 | begin: Alignment.topCenter, 89 | end: Alignment.bottomCenter, 90 | colors: [ 91 | Color(0xfff54242), 92 | Color(0xff912323), 93 | ], 94 | ), 95 | ), 96 | AppTheme( 97 | name: 'Iridescent', 98 | background: LinearGradient( 99 | begin: Alignment.topCenter, 100 | end: Alignment.bottomCenter, 101 | colors: [ 102 | Color(0xffb2b2b2), 103 | Color(0xffb24d00), 104 | Color(0xffb2004d), 105 | Color(0xff004db2), 106 | Color(0xff4d4d4d), 107 | ], 108 | ), 109 | lightTile: Color(0xffabc0d1), 110 | darkTile: Color(0xff7991a6), 111 | moveHint: Color(0xcc004db2), 112 | border: Color(0xff4d5d6b), 113 | ), 114 | AppTheme( 115 | name: 'Black & White', 116 | background: LinearGradient( 117 | begin: Alignment.topCenter, 118 | end: Alignment.bottomCenter, 119 | colors: [ 120 | Color(0xffb2b2b2), 121 | Color(0xff4e4e4e), 122 | ], 123 | ), 124 | lightTile: Color(0xffb2b2b2), 125 | darkTile: Color(0xff808080), 126 | moveHint: Color(0xdd555555), 127 | checkHint: Color(0xff333333), 128 | latestMove: Color(0xdddddddd), 129 | ), 130 | AppTheme( 131 | name: 'Opal', 132 | background: LinearGradient( 133 | begin: Alignment.topCenter, 134 | end: Alignment.bottomCenter, 135 | colors: [ 136 | Color(0xffb5b8c9), 137 | Color(0xffc7958a), 138 | Color(0xffdbd879), 139 | Color(0xff96cf8a), 140 | Color(0xff6a81df), 141 | Color(0xff507fc2), 142 | ], 143 | ), 144 | lightTile: Color(0xffd1d7e0), 145 | darkTile: Color(0xff6494d6), 146 | moveHint: Color(0xcc8ad979), 147 | latestMove: Color(0xddc7958a), 148 | border: Color(0xff9cc5d9), 149 | ), 150 | AppTheme( 151 | name: 'Regal', 152 | background: LinearGradient( 153 | begin: Alignment.topCenter, 154 | end: Alignment.bottomCenter, 155 | colors: [ 156 | Color(0xff2c0078), 157 | Color(0xff6d3ac7), 158 | ], 159 | ), 160 | lightTile: Color(0xfff0c76e), 161 | darkTile: Color(0xff942222), 162 | moveHint: Color(0xcc5d27ba), 163 | checkHint: Color(0xffff0000), 164 | border: Color(0xff6b0707), 165 | ), 166 | AppTheme( 167 | name: 'Vaporwave', 168 | background: LinearGradient( 169 | begin: Alignment.topCenter, 170 | end: Alignment.bottomCenter, 171 | colors: [ 172 | Color(0xffff78ed), 173 | Color(0xff602af5), 174 | ], 175 | ), 176 | lightTile: Color(0xffff78ed), 177 | darkTile: Color(0xff602af5), 178 | moveHint: Color(0xddd1116b), 179 | checkHint: Color(0x80ff0000), 180 | latestMove: Color(0xaa00d0d4), 181 | border: Color(0xff926bff), 182 | ), 183 | AppTheme( 184 | name: 'Dark', 185 | background: LinearGradient( 186 | begin: Alignment.topCenter, 187 | end: Alignment.bottomCenter, 188 | colors: [ 189 | Color(0xff1e1e1e), 190 | Color(0xff2e2e2e), 191 | ], 192 | ), 193 | border: Color(0xff888888), 194 | ), 195 | AppTheme( 196 | name: 'Light', 197 | background: LinearGradient( 198 | begin: Alignment.topCenter, 199 | end: Alignment.bottomCenter, 200 | colors: [ 201 | Color(0xffaeaeae), 202 | Color(0xff8e8e8e), 203 | ], 204 | ), 205 | ), 206 | AppTheme( 207 | name: 'E2-E4', 208 | background: LinearGradient( 209 | begin: Alignment.topCenter, 210 | end: Alignment.bottomCenter, 211 | colors: [ 212 | Color(0xfff9f1c2), 213 | Color(0xff530b0e), 214 | ], 215 | ), 216 | lightTile: Color(0xffd4cb97), 217 | darkTile: Color(0xff73181c), 218 | checkHint: Color(0xffff0000), 219 | moveHint: Color(0xdd666666), 220 | ), 221 | AppTheme( 222 | name: 'Gold Ore', 223 | background: LinearGradient( 224 | begin: Alignment.topCenter, 225 | end: Alignment.bottomCenter, 226 | colors: [ 227 | Color(0xff595959), 228 | Color(0xff807126), 229 | ], 230 | ), 231 | lightTile: Color(0xfff0d656), 232 | darkTile: Color(0xff807126), 233 | moveHint: Color(0xdd444444), 234 | border: Color(0xff5c563c), 235 | ), 236 | AppTheme( 237 | name: 'Aquamarine', 238 | background: LinearGradient( 239 | begin: Alignment.topCenter, 240 | end: Alignment.bottomCenter, 241 | colors: [ 242 | Color(0xff03e8fc), 243 | Color(0xff0345fc), 244 | ], 245 | ), 246 | lightTile: Color(0xff03e8fc), 247 | darkTile: Color(0xff0345fc), 248 | moveHint: Color(0xdd2aad46), 249 | latestMove: Color(0xdd0683d6), 250 | border: Color(0xffc2e3ff), 251 | ), 252 | AppTheme( 253 | name: 'Video Chess', 254 | background: LinearGradient( 255 | begin: Alignment.topCenter, 256 | end: Alignment.bottomCenter, 257 | colors: [ 258 | Color(0xff382db5), 259 | Color(0xff382db5), 260 | ]), 261 | lightTile: Color(0xff584fdb), 262 | darkTile: Color(0xff382db5), 263 | moveHint: Color(0x88c4c6ff), 264 | latestMove: Color(0x88c47937), 265 | ), 266 | AppTheme( 267 | name: 'AMOLED', 268 | background: LinearGradient( 269 | begin: Alignment.topCenter, 270 | end: Alignment.bottomCenter, 271 | colors: [ 272 | Color(0xff000000), 273 | Color(0xff000000), 274 | ], 275 | ), 276 | lightTile: Color(0xff444444), 277 | darkTile: Color(0xff333333), 278 | border: Color(0xff555555), 279 | ), 280 | AppTheme( 281 | name: 'Lewis', 282 | background: LinearGradient( 283 | begin: Alignment.topCenter, 284 | end: Alignment.bottomCenter, 285 | colors: [ 286 | Color(0xff423428), 287 | Color(0xff423428), 288 | ], 289 | ), 290 | lightTile: Color(0xffdbd1c1), 291 | darkTile: Color(0xffab3848), 292 | moveHint: Color(0xdd800b0b), 293 | latestMove: Color(0xddcc9c6c), 294 | border: Color(0xffbdaa8c), 295 | ), 296 | AppTheme( 297 | name: 'Neon', 298 | background: LinearGradient( 299 | begin: Alignment.topCenter, 300 | end: Alignment.bottomCenter, 301 | colors: [ 302 | Color(0xffb734eb), 303 | Color(0xff34d3eb), 304 | Color(0xff34eb62), 305 | ], 306 | ), 307 | lightTile: Color(0xff34d3eb), 308 | darkTile: Color(0xffb734eb), 309 | moveHint: Color(0xdd34eb62), 310 | latestMove: Color(0xddd9eb34), 311 | ), 312 | AppTheme( 313 | name: 'Pink Marble', 314 | background: LinearGradient( 315 | begin: Alignment.topCenter, 316 | end: Alignment.bottomCenter, 317 | colors: [ 318 | Color(0xff45a881), 319 | Color(0xff2782b0), 320 | ], 321 | ), 322 | lightTile: Color(0xffebc0c0), 323 | darkTile: Color(0xff472d22), 324 | moveHint: Color(0xaa45a881), 325 | latestMove: Color(0xaa2782b0), 326 | border: Color(0xffebc0c0), 327 | ), 328 | AppTheme( 329 | name: 'Cherry-Coloured Funk', 330 | background: LinearGradient( 331 | begin: Alignment.topCenter, 332 | end: Alignment.bottomCenter, 333 | colors: [ 334 | Color(0xff434783), 335 | Color(0xffdc3b39), 336 | ], 337 | ), 338 | lightTile: Color(0xffdb5e5c), 339 | darkTile: Color(0xff645183), 340 | moveHint: Color(0xaabdacce), 341 | latestMove: Color(0xaaf0b35d), 342 | border: Color(0xff434783), 343 | ), 344 | ]; 345 | themeList.sort((a, b) => a.name?.compareTo(b.name ?? "") ?? 0); 346 | return themeList; 347 | } 348 | -------------------------------------------------------------------------------- /lib/logic/chess_game.dart: -------------------------------------------------------------------------------- 1 | import 'package:async/async.dart'; 2 | import 'package:en_passant/logic/chess_piece_sprite.dart'; 3 | import 'package:en_passant/logic/move_calculation/ai_move_calculation.dart'; 4 | import 'package:en_passant/logic/move_calculation/move_calculation.dart'; 5 | import 'package:en_passant/logic/move_calculation/move_classes/move_meta.dart'; 6 | import 'package:en_passant/logic/shared_functions.dart'; 7 | import 'package:en_passant/model/app_model.dart'; 8 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 9 | import 'package:flame/events.dart'; 10 | import 'package:flame/game.dart'; 11 | import 'package:flutter/cupertino.dart'; 12 | import 'package:flutter/foundation.dart'; 13 | 14 | import 'chess_board.dart'; 15 | import 'chess_piece.dart'; 16 | import 'move_calculation/move_classes/move.dart'; 17 | 18 | class ChessGame extends Game with TapDetector { 19 | double? width; 20 | double? tileSize; 21 | AppModel appModel; 22 | BuildContext context; 23 | ChessBoard board = ChessBoard(); 24 | Map spriteMap = Map(); 25 | 26 | CancelableOperation? aiOperation; 27 | List validMoves = []; 28 | ChessPiece? selectedPiece; 29 | int? checkHintTile; 30 | Move? latestMove; 31 | 32 | ChessGame(this.appModel, this.context) { 33 | width = MediaQuery.of(context).size.width - 68; 34 | tileSize = (width ?? 0) / 8; 35 | for (var piece in board.player1Pieces + board.player2Pieces) { 36 | spriteMap[piece] = ChessPieceSprite(piece, appModel.pieceTheme); 37 | } 38 | _initSpritePositions(); 39 | if (appModel.isAIsTurn) { 40 | _aiMove(); 41 | } 42 | } 43 | 44 | @override 45 | void onTapDown(TapDownInfo details) { 46 | if (appModel.gameOver || !(appModel.isAIsTurn)) { 47 | var tile = _vector2ToTile(details.eventPosition.widget); 48 | var touchedPiece = board.tiles[tile]; 49 | if (touchedPiece == selectedPiece) { 50 | validMoves = []; 51 | selectedPiece = null; 52 | } else { 53 | if (selectedPiece != null && 54 | touchedPiece != null && 55 | touchedPiece.player == selectedPiece?.player) { 56 | if (validMoves.contains(tile)) { 57 | _movePiece(tile); 58 | } else { 59 | validMoves = []; 60 | _selectPiece(touchedPiece); 61 | } 62 | } else if (selectedPiece == null) { 63 | _selectPiece(touchedPiece); 64 | } else { 65 | _movePiece(tile); 66 | } 67 | } 68 | } 69 | } 70 | 71 | @override 72 | void render(Canvas canvas) { 73 | _drawBoard(canvas); 74 | if (appModel.showHints) { 75 | _drawCheckHint(canvas); 76 | _drawLatestMove(canvas); 77 | } 78 | _drawSelectedPieceHint(canvas); 79 | _drawPieces(canvas); 80 | if (appModel.showHints) { 81 | _drawMoveHints(canvas); 82 | } 83 | } 84 | 85 | @override 86 | void update(double t) { 87 | for (var piece in board.player1Pieces + board.player2Pieces) { 88 | spriteMap[piece]?.update(tileSize ?? 0, appModel, piece); 89 | } 90 | } 91 | 92 | void _initSpritePositions() { 93 | for (var piece in board.player1Pieces + board.player2Pieces) { 94 | spriteMap[piece]?.initSpritePosition(tileSize ?? 0, appModel); 95 | } 96 | } 97 | 98 | void _selectPiece(ChessPiece? piece) { 99 | if (piece != null) { 100 | if (piece.player == appModel.turn) { 101 | selectedPiece = piece; 102 | if (selectedPiece != null) { 103 | validMoves = movesForPiece(piece, board); 104 | } 105 | if (validMoves.isEmpty) { 106 | selectedPiece = null; 107 | } 108 | } 109 | } 110 | } 111 | 112 | void _movePiece(int tile) { 113 | if (validMoves.contains(tile)) { 114 | validMoves = []; 115 | var meta = 116 | push(Move(selectedPiece?.tile ?? 0, tile), board, getMeta: true); 117 | if (meta.promotion) { 118 | appModel.requestPromotion(); 119 | } 120 | _moveCompletion(meta, changeTurn: !meta.promotion); 121 | } 122 | } 123 | 124 | void _aiMove() async { 125 | await Future.delayed(Duration(milliseconds: 500)); 126 | var args = Map(); 127 | args['aiPlayer'] = appModel.aiTurn; 128 | args['aiDifficulty'] = appModel.aiDifficulty; 129 | args['board'] = board; 130 | aiOperation = CancelableOperation.fromFuture( 131 | compute(calculateAIMove, args), 132 | ); 133 | aiOperation?.value.then((move) { 134 | if (move == null || appModel.gameOver) { 135 | appModel.endGame(); 136 | } else { 137 | validMoves = []; 138 | var meta = push(move, board, getMeta: true); 139 | _moveCompletion(meta, changeTurn: !meta.promotion); 140 | if (meta.promotion) { 141 | promote(move.promotionType); 142 | } 143 | } 144 | }); 145 | } 146 | 147 | void cancelAIMove() { 148 | aiOperation?.cancel(); 149 | } 150 | 151 | void undoMove() { 152 | board.redoStack.add(pop(board)); 153 | if (appModel.moveMetaList.length > 1) { 154 | var meta = appModel.moveMetaList[appModel.moveMetaList.length - 2]; 155 | _moveCompletion(meta, clearRedo: false, undoing: true); 156 | } else { 157 | _undoOpeningMove(); 158 | appModel.changeTurn(); 159 | } 160 | } 161 | 162 | void undoTwoMoves() { 163 | board.redoStack.add(pop(board)); 164 | board.redoStack.add(pop(board)); 165 | appModel.popMoveMeta(); 166 | if (appModel.moveMetaList.length > 1) { 167 | _moveCompletion(appModel.moveMetaList[appModel.moveMetaList.length - 2], 168 | clearRedo: false, undoing: true, changeTurn: false); 169 | } else { 170 | _undoOpeningMove(); 171 | } 172 | } 173 | 174 | void _undoOpeningMove() { 175 | selectedPiece = null; 176 | validMoves = []; 177 | latestMove = null; 178 | checkHintTile = null; 179 | appModel.popMoveMeta(); 180 | } 181 | 182 | void redoMove() { 183 | _moveCompletion(pushMSO(board.redoStack.removeLast(), board), 184 | clearRedo: false); 185 | } 186 | 187 | void redoTwoMoves() { 188 | _moveCompletion(pushMSO(board.redoStack.removeLast(), board), 189 | clearRedo: false, updateMetaList: true); 190 | _moveCompletion(pushMSO(board.redoStack.removeLast(), board), 191 | clearRedo: false, updateMetaList: true); 192 | } 193 | 194 | void promote(ChessPieceType type) { 195 | board.moveStack.last.movedPiece?.type = type; 196 | board.moveStack.last.promotionType = type; 197 | addPromotedPiece(board, board.moveStack.last); 198 | appModel.moveMetaList.last.promotionType = type; 199 | _moveCompletion(appModel.moveMetaList.last, updateMetaList: false); 200 | } 201 | 202 | void _moveCompletion( 203 | MoveMeta meta, { 204 | bool clearRedo = true, 205 | bool undoing = false, 206 | bool changeTurn = true, 207 | bool updateMetaList = true, 208 | }) { 209 | if (clearRedo) { 210 | board.redoStack = []; 211 | } 212 | validMoves = []; 213 | latestMove = meta.move; 214 | checkHintTile = null; 215 | var oppositeTurn = oppositePlayer(appModel.turn); 216 | if (kingInCheck(oppositeTurn, board)) { 217 | meta.isCheck = true; 218 | checkHintTile = kingForPlayer(oppositeTurn, board)?.tile; 219 | } 220 | if (kingInCheckmate(oppositeTurn, board)) { 221 | if (!meta.isCheck) { 222 | appModel.stalemate = true; 223 | meta.isStalemate = true; 224 | } 225 | meta.isCheck = false; 226 | meta.isCheckmate = true; 227 | appModel.endGame(); 228 | } 229 | if (undoing) { 230 | appModel.popMoveMeta(); 231 | appModel.undoEndGame(); 232 | } else if (updateMetaList) { 233 | appModel.pushMoveMeta(meta); 234 | } 235 | if (changeTurn) { 236 | appModel.changeTurn(); 237 | } 238 | selectedPiece = null; 239 | if (appModel.isAIsTurn && clearRedo && changeTurn) { 240 | _aiMove(); 241 | } 242 | } 243 | 244 | int _vector2ToTile(Vector2 vector2) { 245 | if (appModel.flip && 246 | appModel.playingWithAI && 247 | appModel.playerSide == Player.player2) { 248 | return (7 - (vector2.y / (tileSize ?? 0)).floor()) * 8 + 249 | (7 - (vector2.x / (tileSize ?? 0)).floor()); 250 | } else { 251 | return (vector2.y / (tileSize ?? 0)).floor() * 8 + 252 | (vector2.x / (tileSize ?? 0)).floor(); 253 | } 254 | } 255 | 256 | void _drawBoard(Canvas canvas) { 257 | for (int tileNo = 0; tileNo < 64; tileNo++) { 258 | canvas.drawRect( 259 | Rect.fromLTWH( 260 | (tileNo % 8) * (tileSize ?? 0), 261 | (tileNo / 8).floor() * (tileSize ?? 0), 262 | (tileSize ?? 0), 263 | (tileSize ?? 0), 264 | ), 265 | Paint() 266 | ..color = (tileNo + (tileNo / 8).floor()) % 2 == 0 267 | ? appModel.theme.lightTile 268 | : appModel.theme.darkTile, 269 | ); 270 | } 271 | } 272 | 273 | void _drawPieces(Canvas canvas) { 274 | for (var piece in board.player1Pieces + board.player2Pieces) { 275 | spriteMap[piece]?.sprite?.render( 276 | canvas, 277 | size: Vector2((tileSize ?? 0) - 10, (tileSize ?? 0) - 10), 278 | position: Vector2( 279 | (spriteMap[piece]?.spriteX ?? 0) + 5, 280 | (spriteMap[piece]?.spriteY ?? 0) + 5, 281 | ), 282 | ); 283 | } 284 | } 285 | 286 | void _drawMoveHints(Canvas canvas) { 287 | for (var tile in validMoves) { 288 | canvas.drawCircle( 289 | Offset( 290 | getXFromTile(tile, (tileSize ?? 0), appModel) + ((tileSize ?? 0) / 2), 291 | getYFromTile(tile, (tileSize ?? 0), appModel) + ((tileSize ?? 0) / 2), 292 | ), 293 | (tileSize ?? 0) / 5, 294 | Paint()..color = appModel.theme.moveHint, 295 | ); 296 | } 297 | } 298 | 299 | void _drawLatestMove(Canvas canvas) { 300 | if (latestMove != null) { 301 | canvas.drawRect( 302 | Rect.fromLTWH( 303 | getXFromTile(latestMove!.from, tileSize ?? 0, appModel), 304 | getYFromTile(latestMove!.from, tileSize ?? 0, appModel), 305 | tileSize ?? 0, 306 | tileSize ?? 0, 307 | ), 308 | Paint()..color = appModel.theme.latestMove, 309 | ); 310 | canvas.drawRect( 311 | Rect.fromLTWH( 312 | getXFromTile(latestMove!.to, tileSize ?? 0, appModel), 313 | getYFromTile(latestMove!.to, tileSize ?? 0, appModel), 314 | tileSize ?? 0, 315 | tileSize ?? 0, 316 | ), 317 | Paint()..color = appModel.theme.latestMove, 318 | ); 319 | } 320 | } 321 | 322 | void _drawCheckHint(Canvas canvas) { 323 | if (checkHintTile != null) { 324 | canvas.drawRect( 325 | Rect.fromLTWH( 326 | getXFromTile(checkHintTile!, tileSize ?? 0, appModel), 327 | getYFromTile(checkHintTile!, tileSize ?? 0, appModel), 328 | tileSize ?? 0, 329 | tileSize ?? 0, 330 | ), 331 | Paint()..color = appModel.theme.checkHint, 332 | ); 333 | } 334 | } 335 | 336 | void _drawSelectedPieceHint(Canvas canvas) { 337 | if (selectedPiece != null) { 338 | canvas.drawRect( 339 | Rect.fromLTWH( 340 | getXFromTile(selectedPiece!.tile, tileSize ?? 0, appModel), 341 | getYFromTile(selectedPiece!.tile, tileSize ?? 0, appModel), 342 | tileSize ?? 0, 343 | tileSize ?? 0, 344 | ), 345 | Paint()..color = appModel.theme.moveHint, 346 | ); 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /lib/logic/chess_board.dart: -------------------------------------------------------------------------------- 1 | import 'package:en_passant/logic/move_calculation/move_classes/move_stack_object.dart'; 2 | import 'package:en_passant/logic/move_calculation/openings.dart'; 3 | import 'package:en_passant/logic/shared_functions.dart'; 4 | import 'package:en_passant/views/components/main_menu_view/game_options/side_picker.dart'; 5 | 6 | import 'chess_piece.dart'; 7 | import 'move_calculation/move_calculation.dart'; 8 | import 'move_calculation/move_classes/move.dart'; 9 | import 'move_calculation/move_classes/move_meta.dart'; 10 | import 'move_calculation/piece_square_tables.dart'; 11 | 12 | const KING_ROW_PIECES = [ 13 | ChessPieceType.rook, 14 | ChessPieceType.knight, 15 | ChessPieceType.bishop, 16 | ChessPieceType.queen, 17 | ChessPieceType.king, 18 | ChessPieceType.bishop, 19 | ChessPieceType.knight, 20 | ChessPieceType.rook 21 | ]; 22 | 23 | class ChessBoard { 24 | List tiles = List.filled(64, null); 25 | List moveStack = []; 26 | List redoStack = []; 27 | List player1Pieces = []; 28 | List player2Pieces = []; 29 | List player1Rooks = []; 30 | List player2Rooks = []; 31 | List player1Queens = []; 32 | List player2Queens = []; 33 | ChessPiece? player1King; 34 | ChessPiece? player2King; 35 | ChessPiece? enPassantPiece; 36 | bool player1KingInCheck = false; 37 | bool player2KingInCheck = false; 38 | List> possibleOpenings = List.from(openings); 39 | int moveCount = 0; 40 | 41 | ChessBoard() { 42 | _addPiecesForPlayer(Player.player1); 43 | _addPiecesForPlayer(Player.player2); 44 | } 45 | 46 | void _addPiecesForPlayer(Player player) { 47 | var kingRowOffset = player == Player.player1 ? 56 : 0; 48 | var pawnRowOffset = player == Player.player1 ? -8 : 8; 49 | var index = 0; 50 | for (var pieceType in KING_ROW_PIECES) { 51 | var id = player == Player.player1 ? index * 2 : index * 2 + 16; 52 | var piece = ChessPiece(id, pieceType, player, kingRowOffset + index); 53 | var pawn = ChessPiece(id + 1, ChessPieceType.pawn, player, 54 | kingRowOffset + pawnRowOffset + index); 55 | _setTile(piece.tile, piece, this); 56 | _setTile(pawn.tile, pawn, this); 57 | piecesForPlayer(player, this).addAll([piece, pawn]); 58 | if (piece.type == ChessPieceType.king) { 59 | player == Player.player1 ? player1King = piece : player2King = piece; 60 | } else if (piece.type == ChessPieceType.queen) { 61 | _queensForPlayer(player, this).add(piece); 62 | } else if (piece.type == ChessPieceType.rook) { 63 | rooksForPlayer(player, this).add(piece); 64 | } 65 | index++; 66 | } 67 | } 68 | } 69 | 70 | int boardValue(ChessBoard board) { 71 | int value = 0; 72 | for (var piece in board.player1Pieces + board.player2Pieces) { 73 | value += piece.value + squareValue(piece, _inEndGame(board)); 74 | } 75 | return value; 76 | } 77 | 78 | MoveMeta push(Move move, ChessBoard board, 79 | {bool getMeta = false, 80 | ChessPieceType promotionType = ChessPieceType.promotion}) { 81 | var mso = MoveStackObject(move, board.tiles[move.from], board.tiles[move.to], 82 | board.enPassantPiece, List.from(board.possibleOpenings)); 83 | var meta = MoveMeta(move, mso.movedPiece?.player, mso.movedPiece?.type); 84 | if (board.possibleOpenings.isNotEmpty) { 85 | _filterPossibleOpenings(board, move); 86 | } 87 | if (getMeta) { 88 | _checkMoveAmbiguity(move, meta, board); 89 | } 90 | if (_castled(mso.movedPiece, mso.takenPiece)) { 91 | _castle(board, mso, meta); 92 | } else { 93 | _standardMove(board, mso, meta); 94 | if (mso.movedPiece?.type == ChessPieceType.pawn) { 95 | if (_promotion(mso.movedPiece)) { 96 | mso.promotionType = promotionType; 97 | meta.promotionType = promotionType; 98 | _promote(board, mso, meta); 99 | } 100 | _checkEnPassant(board, mso, meta); 101 | } 102 | } 103 | if (_canTakeEnPassant(mso.movedPiece)) { 104 | board.enPassantPiece = mso.movedPiece; 105 | } else { 106 | board.enPassantPiece = null; 107 | } 108 | if (meta.type == ChessPieceType.pawn && meta.took) { 109 | meta.rowIsAmbiguous = true; 110 | } 111 | board.moveStack.add(mso); 112 | board.moveCount++; 113 | return meta; 114 | } 115 | 116 | MoveMeta pushMSO(MoveStackObject mso, ChessBoard board) { 117 | return push(mso.move, board, 118 | promotionType: mso.promotionType ?? ChessPieceType.promotion); 119 | } 120 | 121 | MoveStackObject pop(ChessBoard board) { 122 | var mso = board.moveStack.removeLast(); 123 | board.enPassantPiece = mso.enPassantPiece; 124 | board.possibleOpenings = mso.possibleOpenings ?? []; 125 | if (mso.castled) { 126 | _undoCastle(board, mso); 127 | } else { 128 | _undoStandardMove(board, mso); 129 | if (mso.promotion) { 130 | _undoPromote(board, mso); 131 | } 132 | if (mso.enPassant) { 133 | _addPiece(mso.enPassantPiece, board); 134 | _setTile(mso.enPassantPiece?.tile, mso.enPassantPiece, board); 135 | } 136 | } 137 | board.moveCount--; 138 | return mso; 139 | } 140 | 141 | void _standardMove(ChessBoard board, MoveStackObject mso, MoveMeta meta) { 142 | _setTile(mso.move.to, mso.movedPiece, board); 143 | _setTile(mso.move.from, null, board); 144 | mso.movedPiece?.moveCount++; 145 | if (mso.takenPiece != null) { 146 | _removePiece(mso.takenPiece, board); 147 | meta.took = true; 148 | } 149 | } 150 | 151 | void _undoStandardMove(ChessBoard board, MoveStackObject mso) { 152 | _setTile(mso.move.from, mso.movedPiece, board); 153 | _setTile(mso.move.to, null, board); 154 | if (mso.takenPiece != null) { 155 | _addPiece(mso.takenPiece, board); 156 | _setTile(mso.move.to, mso.takenPiece, board); 157 | } 158 | mso.movedPiece?.moveCount--; 159 | } 160 | 161 | void _castle(ChessBoard board, MoveStackObject mso, MoveMeta meta) { 162 | var king = mso.movedPiece?.type == ChessPieceType.king 163 | ? mso.movedPiece 164 | : mso.takenPiece; 165 | var rook = mso.movedPiece?.type == ChessPieceType.rook 166 | ? mso.movedPiece 167 | : mso.takenPiece; 168 | _setTile(king?.tile, null, board); 169 | _setTile(rook?.tile, null, board); 170 | var kingCol = tileToCol(rook?.tile ?? 0) == 0 ? 2 : 6; 171 | var rookCol = tileToCol(rook?.tile ?? 0) == 0 ? 3 : 5; 172 | _setTile(tileToRow(king?.tile ?? 0) * 8 + kingCol, king, board); 173 | _setTile(tileToRow(rook?.tile ?? 0) * 8 + rookCol, rook, board); 174 | tileToCol(rook?.tile ?? 0) == 3 175 | ? meta.queenCastle = true 176 | : meta.kingCastle = true; 177 | king?.moveCount++; 178 | rook?.moveCount++; 179 | mso.castled = true; 180 | } 181 | 182 | void _undoCastle(ChessBoard board, MoveStackObject mso) { 183 | var king = mso.movedPiece?.type == ChessPieceType.king 184 | ? mso.movedPiece 185 | : mso.takenPiece; 186 | var rook = mso.movedPiece?.type == ChessPieceType.rook 187 | ? mso.movedPiece 188 | : mso.takenPiece; 189 | _setTile(king?.tile, null, board); 190 | _setTile(rook?.tile, null, board); 191 | var rookCol = tileToCol(rook?.tile ?? 0) == 3 ? 0 : 7; 192 | _setTile(tileToRow(king?.tile ?? 0) * 8 + 4, king, board); 193 | _setTile(tileToRow(rook?.tile ?? 0) * 8 + rookCol, rook, board); 194 | king?.moveCount--; 195 | rook?.moveCount--; 196 | } 197 | 198 | void _promote(ChessBoard board, MoveStackObject mso, MoveMeta meta) { 199 | mso.movedPiece?.type = mso.promotionType ?? ChessPieceType.promotion; 200 | if (mso.promotionType != ChessPieceType.promotion) { 201 | addPromotedPiece(board, mso); 202 | } 203 | meta.promotion = true; 204 | mso.promotion = true; 205 | } 206 | 207 | void addPromotedPiece(ChessBoard board, MoveStackObject mso) { 208 | switch (mso.promotionType) { 209 | case ChessPieceType.queen: 210 | { 211 | if (mso.movedPiece != null) { 212 | _queensForPlayer(mso.movedPiece?.player ?? Player.player1, board) 213 | .add(mso.movedPiece!); 214 | } 215 | } 216 | break; 217 | case ChessPieceType.rook: 218 | { 219 | if (mso.movedPiece != null) { 220 | rooksForPlayer(mso.movedPiece?.player ?? Player.player1, board) 221 | .add(mso.movedPiece!); 222 | } 223 | } 224 | break; 225 | default: 226 | {} 227 | } 228 | } 229 | 230 | void _undoPromote(ChessBoard board, MoveStackObject mso) { 231 | mso.movedPiece?.type = ChessPieceType.pawn; 232 | switch (mso.promotionType) { 233 | case ChessPieceType.queen: 234 | { 235 | _queensForPlayer(mso.movedPiece?.player ?? Player.player1, board) 236 | .remove(mso.movedPiece); 237 | } 238 | break; 239 | case ChessPieceType.rook: 240 | { 241 | rooksForPlayer(mso.movedPiece?.player ?? Player.player1, board) 242 | .remove(mso.movedPiece); 243 | } 244 | break; 245 | default: 246 | {} 247 | } 248 | } 249 | 250 | void _checkEnPassant(ChessBoard board, MoveStackObject mso, MoveMeta meta) { 251 | var offset = mso.movedPiece?.player == Player.player1 ? 8 : -8; 252 | var tile = (mso.movedPiece?.tile ?? 0) + offset; 253 | var takenPiece = board.tiles[tile]; 254 | if (takenPiece != null && takenPiece == board.enPassantPiece) { 255 | _removePiece(takenPiece, board); 256 | _setTile(takenPiece.tile, null, board); 257 | mso.enPassant = true; 258 | } 259 | } 260 | 261 | void _checkMoveAmbiguity(Move move, MoveMeta moveMeta, ChessBoard board) { 262 | var piece = board.tiles[move.from]; 263 | for (var otherPiece 264 | in _piecesOfTypeForPlayer(piece?.type, piece?.player, board)) { 265 | if (piece != otherPiece) { 266 | if (movesForPiece(otherPiece, board).contains(move.to)) { 267 | if (tileToCol(otherPiece.tile) == tileToCol(piece?.tile ?? 0)) { 268 | moveMeta.colIsAmbiguous = true; 269 | } else { 270 | moveMeta.rowIsAmbiguous = true; 271 | } 272 | } 273 | } 274 | } 275 | } 276 | 277 | void _filterPossibleOpenings(ChessBoard board, Move move) { 278 | board.possibleOpenings = board.possibleOpenings 279 | .where((opening) => 280 | opening[board.moveCount] == move && 281 | opening.length > board.moveCount + 1) 282 | .toList(); 283 | } 284 | 285 | void _setTile(int? tile, ChessPiece? piece, ChessBoard board) { 286 | if (tile != null) { 287 | board.tiles[tile] = piece; 288 | } 289 | if (piece != null) { 290 | piece.tile = tile ?? 0; 291 | } 292 | } 293 | 294 | void _addPiece(ChessPiece? piece, ChessBoard board) { 295 | if (piece != null) { 296 | piecesForPlayer(piece.player, board).add(piece); 297 | if (piece.type == ChessPieceType.rook) { 298 | rooksForPlayer(piece.player, board).add(piece); 299 | } 300 | if (piece.type == ChessPieceType.queen) { 301 | _queensForPlayer(piece.player, board).add(piece); 302 | } 303 | } 304 | } 305 | 306 | void _removePiece(ChessPiece? piece, ChessBoard board) { 307 | if (piece != null) { 308 | piecesForPlayer(piece.player, board).remove(piece); 309 | if (piece.type == ChessPieceType.rook) { 310 | rooksForPlayer(piece.player, board).remove(piece); 311 | } 312 | if (piece.type == ChessPieceType.queen) { 313 | _queensForPlayer(piece.player, board).remove(piece); 314 | } 315 | } 316 | } 317 | 318 | List piecesForPlayer(Player player, ChessBoard board) { 319 | return player == Player.player1 ? board.player1Pieces : board.player2Pieces; 320 | } 321 | 322 | ChessPiece? kingForPlayer(Player player, ChessBoard board) { 323 | return player == Player.player1 ? board.player1King : board.player2King; 324 | } 325 | 326 | List rooksForPlayer(Player player, ChessBoard board) { 327 | return player == Player.player1 ? board.player1Rooks : board.player2Rooks; 328 | } 329 | 330 | List _queensForPlayer(Player player, ChessBoard board) { 331 | return player == Player.player1 ? board.player1Queens : board.player2Queens; 332 | } 333 | 334 | List _piecesOfTypeForPlayer( 335 | ChessPieceType? type, Player? player, ChessBoard board) { 336 | List pieces = []; 337 | if (type != null && player != null) { 338 | for (var piece in piecesForPlayer(player, board)) { 339 | if (piece.type == type) { 340 | pieces.add(piece); 341 | } 342 | } 343 | } 344 | return pieces; 345 | } 346 | 347 | bool _castled(ChessPiece? movedPiece, ChessPiece? takenPiece) { 348 | return takenPiece != null && takenPiece.player == movedPiece?.player; 349 | } 350 | 351 | bool _promotion(ChessPiece? movedPiece) { 352 | return movedPiece?.type == ChessPieceType.pawn && 353 | (tileToRow(movedPiece?.tile ?? 0) == 7 || 354 | tileToRow(movedPiece?.tile ?? 0) == 0); 355 | } 356 | 357 | bool _canTakeEnPassant(ChessPiece? movedPiece) { 358 | return movedPiece?.moveCount == 1 && 359 | movedPiece?.type == ChessPieceType.pawn && 360 | (tileToRow(movedPiece?.tile ?? 0) == 3 || 361 | tileToRow(movedPiece?.tile ?? 0) == 4); 362 | } 363 | 364 | bool _inEndGame(ChessBoard board) { 365 | return (_queensForPlayer(Player.player1, board).isEmpty && 366 | _queensForPlayer(Player.player2, board).isEmpty) || 367 | piecesForPlayer(Player.player1, board).length <= 3 || 368 | piecesForPlayer(Player.player2, board).length <= 3; 369 | } 370 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3C44AB63D4EFC8CB2C5AA18B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDEA1FE002B71AC8DA98877F /* Pods_Runner.framework */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 35 | 31CDE7A3FC45BD8481EFF020 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 37 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 38 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 40 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 41 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 42 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 44 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 46 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | C88316130FDE678B7DA9DF23 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 48 | DD0A2C228581C3B9DF1CF17A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 49 | F7EE8C1B25A2D68100211DE9 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 50 | FDEA1FE002B71AC8DA98877F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | 3C44AB63D4EFC8CB2C5AA18B /* Pods_Runner.framework in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXFrameworksBuildPhase section */ 63 | 64 | /* Begin PBXGroup section */ 65 | 482B0E5AF436022460FB3D5A /* Pods */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 31CDE7A3FC45BD8481EFF020 /* Pods-Runner.debug.xcconfig */, 69 | DD0A2C228581C3B9DF1CF17A /* Pods-Runner.release.xcconfig */, 70 | C88316130FDE678B7DA9DF23 /* Pods-Runner.profile.xcconfig */, 71 | ); 72 | path = Pods; 73 | sourceTree = ""; 74 | }; 75 | 9740EEB11CF90186004384FC /* Flutter */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 79 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 80 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 81 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 82 | ); 83 | name = Flutter; 84 | sourceTree = ""; 85 | }; 86 | 97C146E51CF9000F007C117D = { 87 | isa = PBXGroup; 88 | children = ( 89 | 9740EEB11CF90186004384FC /* Flutter */, 90 | 97C146F01CF9000F007C117D /* Runner */, 91 | 97C146EF1CF9000F007C117D /* Products */, 92 | 482B0E5AF436022460FB3D5A /* Pods */, 93 | FC61BF6918D204E06EF93CBB /* Frameworks */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 97C146EF1CF9000F007C117D /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 97C146EE1CF9000F007C117D /* Runner.app */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 97C146F01CF9000F007C117D /* Runner */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | F7EE8C1B25A2D68100211DE9 /* Runner.entitlements */, 109 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 110 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 111 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 112 | 97C147021CF9000F007C117D /* Info.plist */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | FC61BF6918D204E06EF93CBB /* Frameworks */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | FDEA1FE002B71AC8DA98877F /* Pods_Runner.framework */, 125 | ); 126 | name = Frameworks; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | 97C146ED1CF9000F007C117D /* Runner */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 135 | buildPhases = ( 136 | AA9EF834C0F29F5E2941E497 /* [CP] Check Pods Manifest.lock */, 137 | 9740EEB61CF901F6004384FC /* Run Script */, 138 | 97C146EA1CF9000F007C117D /* Sources */, 139 | 97C146EB1CF9000F007C117D /* Frameworks */, 140 | 97C146EC1CF9000F007C117D /* Resources */, 141 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 142 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 143 | 5606DDAEC1E3948199FB8624 /* [CP] Embed Pods Frameworks */, 144 | ); 145 | buildRules = ( 146 | ); 147 | dependencies = ( 148 | ); 149 | name = Runner; 150 | productName = Runner; 151 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | /* End PBXNativeTarget section */ 155 | 156 | /* Begin PBXProject section */ 157 | 97C146E61CF9000F007C117D /* Project object */ = { 158 | isa = PBXProject; 159 | attributes = { 160 | LastUpgradeCheck = 1230; 161 | ORGANIZATIONNAME = ""; 162 | TargetAttributes = { 163 | 97C146ED1CF9000F007C117D = { 164 | CreatedOnToolsVersion = 7.3.1; 165 | LastSwiftMigration = 1100; 166 | }; 167 | }; 168 | }; 169 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 170 | compatibilityVersion = "Xcode 9.3"; 171 | developmentRegion = en; 172 | hasScannedForEncodings = 0; 173 | knownRegions = ( 174 | en, 175 | Base, 176 | ); 177 | mainGroup = 97C146E51CF9000F007C117D; 178 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 179 | projectDirPath = ""; 180 | projectRoot = ""; 181 | targets = ( 182 | 97C146ED1CF9000F007C117D /* Runner */, 183 | ); 184 | }; 185 | /* End PBXProject section */ 186 | 187 | /* Begin PBXResourcesBuildPhase section */ 188 | 97C146EC1CF9000F007C117D /* Resources */ = { 189 | isa = PBXResourcesBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 193 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 194 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 195 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | /* End PBXResourcesBuildPhase section */ 200 | 201 | /* Begin PBXShellScriptBuildPhase section */ 202 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 203 | isa = PBXShellScriptBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | inputPaths = ( 208 | ); 209 | name = "Thin Binary"; 210 | outputPaths = ( 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | shellPath = /bin/sh; 214 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 215 | }; 216 | 5606DDAEC1E3948199FB8624 /* [CP] Embed Pods Frameworks */ = { 217 | isa = PBXShellScriptBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | ); 221 | inputFileListPaths = ( 222 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 223 | ); 224 | name = "[CP] Embed Pods Frameworks"; 225 | outputFileListPaths = ( 226 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | shellPath = /bin/sh; 230 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 231 | showEnvVarsInLog = 0; 232 | }; 233 | 9740EEB61CF901F6004384FC /* Run Script */ = { 234 | isa = PBXShellScriptBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | ); 238 | inputPaths = ( 239 | ); 240 | name = "Run Script"; 241 | outputPaths = ( 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | shellPath = /bin/sh; 245 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 246 | }; 247 | AA9EF834C0F29F5E2941E497 /* [CP] Check Pods Manifest.lock */ = { 248 | isa = PBXShellScriptBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | ); 252 | inputFileListPaths = ( 253 | ); 254 | inputPaths = ( 255 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 256 | "${PODS_ROOT}/Manifest.lock", 257 | ); 258 | name = "[CP] Check Pods Manifest.lock"; 259 | outputFileListPaths = ( 260 | ); 261 | outputPaths = ( 262 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | shellPath = /bin/sh; 266 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 267 | showEnvVarsInLog = 0; 268 | }; 269 | /* End PBXShellScriptBuildPhase section */ 270 | 271 | /* Begin PBXSourcesBuildPhase section */ 272 | 97C146EA1CF9000F007C117D /* Sources */ = { 273 | isa = PBXSourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 277 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | /* End PBXSourcesBuildPhase section */ 282 | 283 | /* Begin PBXVariantGroup section */ 284 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 285 | isa = PBXVariantGroup; 286 | children = ( 287 | 97C146FB1CF9000F007C117D /* Base */, 288 | ); 289 | name = Main.storyboard; 290 | sourceTree = ""; 291 | }; 292 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C147001CF9000F007C117D /* Base */, 296 | ); 297 | name = LaunchScreen.storyboard; 298 | sourceTree = ""; 299 | }; 300 | /* End PBXVariantGroup section */ 301 | 302 | /* Begin XCBuildConfiguration section */ 303 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | CLANG_ANALYZER_NONNULL = YES; 308 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 309 | CLANG_CXX_LIBRARY = "libc++"; 310 | CLANG_ENABLE_MODULES = YES; 311 | CLANG_ENABLE_OBJC_ARC = YES; 312 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 313 | CLANG_WARN_BOOL_CONVERSION = YES; 314 | CLANG_WARN_COMMA = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INFINITE_RECURSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 324 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 327 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 328 | CLANG_WARN_STRICT_PROTOTYPES = YES; 329 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 330 | CLANG_WARN_UNREACHABLE_CODE = YES; 331 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 332 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 333 | COPY_PHASE_STRIP = NO; 334 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 335 | ENABLE_NS_ASSERTIONS = NO; 336 | ENABLE_STRICT_OBJC_MSGSEND = YES; 337 | GCC_C_LANGUAGE_STANDARD = gnu99; 338 | GCC_NO_COMMON_BLOCKS = YES; 339 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 340 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 341 | GCC_WARN_UNDECLARED_SELECTOR = YES; 342 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 343 | GCC_WARN_UNUSED_FUNCTION = YES; 344 | GCC_WARN_UNUSED_VARIABLE = YES; 345 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 346 | MTL_ENABLE_DEBUG_INFO = NO; 347 | SDKROOT = iphoneos; 348 | SUPPORTED_PLATFORMS = iphoneos; 349 | TARGETED_DEVICE_FAMILY = "1,2"; 350 | VALIDATE_PRODUCT = YES; 351 | }; 352 | name = Profile; 353 | }; 354 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 355 | isa = XCBuildConfiguration; 356 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 357 | buildSettings = { 358 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 359 | CLANG_ENABLE_MODULES = YES; 360 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 361 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 362 | DEVELOPMENT_TEAM = DKFDXK99VA; 363 | ENABLE_BITCODE = NO; 364 | FRAMEWORK_SEARCH_PATHS = ( 365 | "$(inherited)", 366 | "$(PROJECT_DIR)/Flutter", 367 | ); 368 | INFOPLIST_FILE = Runner/Info.plist; 369 | LD_RUNPATH_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "@executable_path/Frameworks", 372 | ); 373 | LIBRARY_SEARCH_PATHS = ( 374 | "$(inherited)", 375 | "$(PROJECT_DIR)/Flutter", 376 | ); 377 | PRODUCT_BUNDLE_IDENTIFIER = "com.pscottzero.en-passant"; 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | SUPPORTS_MACCATALYST = NO; 380 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | TARGETED_DEVICE_FAMILY = 1; 384 | VERSIONING_SYSTEM = "apple-generic"; 385 | }; 386 | name = Profile; 387 | }; 388 | 97C147031CF9000F007C117D /* Debug */ = { 389 | isa = XCBuildConfiguration; 390 | buildSettings = { 391 | ALWAYS_SEARCH_USER_PATHS = NO; 392 | CLANG_ANALYZER_NONNULL = YES; 393 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 394 | CLANG_CXX_LIBRARY = "libc++"; 395 | CLANG_ENABLE_MODULES = YES; 396 | CLANG_ENABLE_OBJC_ARC = YES; 397 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 398 | CLANG_WARN_BOOL_CONVERSION = YES; 399 | CLANG_WARN_COMMA = YES; 400 | CLANG_WARN_CONSTANT_CONVERSION = YES; 401 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 402 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 403 | CLANG_WARN_EMPTY_BODY = YES; 404 | CLANG_WARN_ENUM_CONVERSION = YES; 405 | CLANG_WARN_INFINITE_RECURSION = YES; 406 | CLANG_WARN_INT_CONVERSION = YES; 407 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 408 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 409 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 411 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 412 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 413 | CLANG_WARN_STRICT_PROTOTYPES = YES; 414 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 415 | CLANG_WARN_UNREACHABLE_CODE = YES; 416 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 417 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 418 | COPY_PHASE_STRIP = NO; 419 | DEBUG_INFORMATION_FORMAT = dwarf; 420 | ENABLE_STRICT_OBJC_MSGSEND = YES; 421 | ENABLE_TESTABILITY = YES; 422 | GCC_C_LANGUAGE_STANDARD = gnu99; 423 | GCC_DYNAMIC_NO_PIC = NO; 424 | GCC_NO_COMMON_BLOCKS = YES; 425 | GCC_OPTIMIZATION_LEVEL = 0; 426 | GCC_PREPROCESSOR_DEFINITIONS = ( 427 | "DEBUG=1", 428 | "$(inherited)", 429 | ); 430 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 431 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 432 | GCC_WARN_UNDECLARED_SELECTOR = YES; 433 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 434 | GCC_WARN_UNUSED_FUNCTION = YES; 435 | GCC_WARN_UNUSED_VARIABLE = YES; 436 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 437 | MTL_ENABLE_DEBUG_INFO = YES; 438 | ONLY_ACTIVE_ARCH = YES; 439 | SDKROOT = iphoneos; 440 | TARGETED_DEVICE_FAMILY = "1,2"; 441 | }; 442 | name = Debug; 443 | }; 444 | 97C147041CF9000F007C117D /* Release */ = { 445 | isa = XCBuildConfiguration; 446 | buildSettings = { 447 | ALWAYS_SEARCH_USER_PATHS = NO; 448 | CLANG_ANALYZER_NONNULL = YES; 449 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 450 | CLANG_CXX_LIBRARY = "libc++"; 451 | CLANG_ENABLE_MODULES = YES; 452 | CLANG_ENABLE_OBJC_ARC = YES; 453 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 454 | CLANG_WARN_BOOL_CONVERSION = YES; 455 | CLANG_WARN_COMMA = YES; 456 | CLANG_WARN_CONSTANT_CONVERSION = YES; 457 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 458 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 459 | CLANG_WARN_EMPTY_BODY = YES; 460 | CLANG_WARN_ENUM_CONVERSION = YES; 461 | CLANG_WARN_INFINITE_RECURSION = YES; 462 | CLANG_WARN_INT_CONVERSION = YES; 463 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 465 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 466 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 467 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 468 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 469 | CLANG_WARN_STRICT_PROTOTYPES = YES; 470 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 471 | CLANG_WARN_UNREACHABLE_CODE = YES; 472 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 473 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 474 | COPY_PHASE_STRIP = NO; 475 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 476 | ENABLE_NS_ASSERTIONS = NO; 477 | ENABLE_STRICT_OBJC_MSGSEND = YES; 478 | GCC_C_LANGUAGE_STANDARD = gnu99; 479 | GCC_NO_COMMON_BLOCKS = YES; 480 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 481 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 482 | GCC_WARN_UNDECLARED_SELECTOR = YES; 483 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 484 | GCC_WARN_UNUSED_FUNCTION = YES; 485 | GCC_WARN_UNUSED_VARIABLE = YES; 486 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 487 | MTL_ENABLE_DEBUG_INFO = NO; 488 | SDKROOT = iphoneos; 489 | SUPPORTED_PLATFORMS = iphoneos; 490 | SWIFT_COMPILATION_MODE = wholemodule; 491 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 492 | TARGETED_DEVICE_FAMILY = "1,2"; 493 | VALIDATE_PRODUCT = YES; 494 | }; 495 | name = Release; 496 | }; 497 | 97C147061CF9000F007C117D /* Debug */ = { 498 | isa = XCBuildConfiguration; 499 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 500 | buildSettings = { 501 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 502 | CLANG_ENABLE_MODULES = YES; 503 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 504 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 505 | DEVELOPMENT_TEAM = DKFDXK99VA; 506 | ENABLE_BITCODE = NO; 507 | FRAMEWORK_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | INFOPLIST_FILE = Runner/Info.plist; 512 | LD_RUNPATH_SEARCH_PATHS = ( 513 | "$(inherited)", 514 | "@executable_path/Frameworks", 515 | ); 516 | LIBRARY_SEARCH_PATHS = ( 517 | "$(inherited)", 518 | "$(PROJECT_DIR)/Flutter", 519 | ); 520 | PRODUCT_BUNDLE_IDENTIFIER = "com.pscottzero.en-passant"; 521 | PRODUCT_NAME = "$(TARGET_NAME)"; 522 | SUPPORTS_MACCATALYST = NO; 523 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 524 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 525 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 526 | SWIFT_VERSION = 5.0; 527 | TARGETED_DEVICE_FAMILY = 1; 528 | VERSIONING_SYSTEM = "apple-generic"; 529 | }; 530 | name = Debug; 531 | }; 532 | 97C147071CF9000F007C117D /* Release */ = { 533 | isa = XCBuildConfiguration; 534 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 535 | buildSettings = { 536 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 537 | CLANG_ENABLE_MODULES = YES; 538 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 539 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 540 | DEVELOPMENT_TEAM = DKFDXK99VA; 541 | ENABLE_BITCODE = NO; 542 | FRAMEWORK_SEARCH_PATHS = ( 543 | "$(inherited)", 544 | "$(PROJECT_DIR)/Flutter", 545 | ); 546 | INFOPLIST_FILE = Runner/Info.plist; 547 | LD_RUNPATH_SEARCH_PATHS = ( 548 | "$(inherited)", 549 | "@executable_path/Frameworks", 550 | ); 551 | LIBRARY_SEARCH_PATHS = ( 552 | "$(inherited)", 553 | "$(PROJECT_DIR)/Flutter", 554 | ); 555 | PRODUCT_BUNDLE_IDENTIFIER = "com.pscottzero.en-passant"; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | SUPPORTS_MACCATALYST = NO; 558 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 559 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 560 | SWIFT_VERSION = 5.0; 561 | TARGETED_DEVICE_FAMILY = 1; 562 | VERSIONING_SYSTEM = "apple-generic"; 563 | }; 564 | name = Release; 565 | }; 566 | /* End XCBuildConfiguration section */ 567 | 568 | /* Begin XCConfigurationList section */ 569 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 570 | isa = XCConfigurationList; 571 | buildConfigurations = ( 572 | 97C147031CF9000F007C117D /* Debug */, 573 | 97C147041CF9000F007C117D /* Release */, 574 | 249021D3217E4FDB00AE95B9 /* Profile */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 580 | isa = XCConfigurationList; 581 | buildConfigurations = ( 582 | 97C147061CF9000F007C117D /* Debug */, 583 | 97C147071CF9000F007C117D /* Release */, 584 | 249021D4217E4FDB00AE95B9 /* Profile */, 585 | ); 586 | defaultConfigurationIsVisible = 0; 587 | defaultConfigurationName = Release; 588 | }; 589 | /* End XCConfigurationList section */ 590 | }; 591 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 592 | } 593 | --------------------------------------------------------------------------------