From 769a477fc79ea7ea2f6a5996ad62e21c246b56f9 Mon Sep 17 00:00:00 2001 From: Jean Bovet Date: Tue, 11 May 2021 20:14:07 -0700 Subject: [PATCH] Support navigating to the start or end of the game --- BChess.xcodeproj/project.pbxproj | 12 +++--- Shared/Actions.swift | 25 ++++-------- Shared/ActionsToolbar.swift | 28 +------------ Shared/Bridge/FEngine.h | 14 ++++--- Shared/Bridge/FEngine.mm | 28 +++++++------ Shared/Engine/Engine/ChessEngine.hpp | 18 +-------- Shared/Engine/Engine/ChessGame.cpp | 42 +++++++++++++------- Shared/Engine/Engine/ChessGame.hpp | 9 +++-- Shared/Views/AnalyzeActionsView.swift | 41 ------------------- Shared/Views/ContentView.swift | 2 +- Shared/Views/InformationView.swift | 1 - Shared/Views/NavigationView.swift | 57 +++++++++++++++++++++++++++ 12 files changed, 130 insertions(+), 147 deletions(-) delete mode 100644 Shared/Views/AnalyzeActionsView.swift create mode 100644 Shared/Views/NavigationView.swift diff --git a/BChess.xcodeproj/project.pbxproj b/BChess.xcodeproj/project.pbxproj index 18e79cd..0f7d579 100644 --- a/BChess.xcodeproj/project.pbxproj +++ b/BChess.xcodeproj/project.pbxproj @@ -25,8 +25,8 @@ A72E2B57200496CD006CBB1C /* BoardHashTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A72E2B56200496CC006CBB1C /* BoardHashTests.cpp */; }; A731BD3720009856004C13EF /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = A731BD3620009856004C13EF /* Tournament.swift */; }; A731BD392000A513004C13EF /* TournamentEngines.swift in Sources */ = {isa = PBXBuildFile; fileRef = A731BD382000A513004C13EF /* TournamentEngines.swift */; }; - A746997C2637CCF7007E0058 /* AnalyzeActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A746997B2637CCF7007E0058 /* AnalyzeActionsView.swift */; }; - A746997D2637CCF7007E0058 /* AnalyzeActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A746997B2637CCF7007E0058 /* AnalyzeActionsView.swift */; }; + A746997C2637CCF7007E0058 /* NavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A746997B2637CCF7007E0058 /* NavigationView.swift */; }; + A746997D2637CCF7007E0058 /* NavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A746997B2637CCF7007E0058 /* NavigationView.swift */; }; A746C244200148E9001F4437 /* ChessBoardHash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A746C242200148E9001F4437 /* ChessBoardHash.cpp */; }; A746C245200148ED001F4437 /* ChessBoardHash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A746C242200148E9001F4437 /* ChessBoardHash.cpp */; }; A746C2492001707F001F4437 /* GameHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A746C2472001707F001F4437 /* GameHistory.cpp */; }; @@ -209,7 +209,7 @@ A72E2B56200496CC006CBB1C /* BoardHashTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BoardHashTests.cpp; sourceTree = ""; }; A731BD3620009856004C13EF /* Tournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tournament.swift; sourceTree = ""; }; A731BD382000A513004C13EF /* TournamentEngines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentEngines.swift; sourceTree = ""; }; - A746997B2637CCF7007E0058 /* AnalyzeActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyzeActionsView.swift; sourceTree = ""; }; + A746997B2637CCF7007E0058 /* NavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationView.swift; sourceTree = ""; }; A746C242200148E9001F4437 /* ChessBoardHash.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChessBoardHash.cpp; sourceTree = ""; }; A746C243200148E9001F4437 /* ChessBoardHash.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ChessBoardHash.hpp; sourceTree = ""; }; A746C2472001707F001F4437 /* GameHistory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GameHistory.cpp; sourceTree = ""; }; @@ -561,7 +561,7 @@ A7A30EDC25AEBD7C00729432 /* LabelsView.swift */, A77F66AE25A832240030E61D /* ContentView.swift */, A7A30F2E25B2BAFC00729432 /* PromotionView.swift */, - A746997B2637CCF7007E0058 /* AnalyzeActionsView.swift */, + A746997B2637CCF7007E0058 /* NavigationView.swift */, ); path = Views; sourceTree = ""; @@ -885,7 +885,7 @@ A7FE322A25AA96D100A75936 /* ChessBoardHash.cpp in Sources */, A77F66D725A832270030E61D /* ChessDocument.swift in Sources */, A795162225ABE59900AEA95F /* PlayAgainst.swift in Sources */, - A746997C2637CCF7007E0058 /* AnalyzeActionsView.swift in Sources */, + A746997C2637CCF7007E0058 /* NavigationView.swift in Sources */, A7FE324E25AAD61200A75936 /* FEngineUtility.mm in Sources */, A79515F625ABE44000AEA95F /* PiecesView.swift in Sources */, A7BC72E42636003F008FBBB4 /* NewGameView.swift in Sources */, @@ -937,7 +937,7 @@ A7FE31DA25AA96B800A75936 /* MoveList.cpp in Sources */, A795162325ABE59900AEA95F /* PlayAgainst.swift in Sources */, A7FE31CB25AA96A800A75936 /* FEngineUtility.mm in Sources */, - A746997D2637CCF7007E0058 /* AnalyzeActionsView.swift in Sources */, + A746997D2637CCF7007E0058 /* NavigationView.swift in Sources */, A79515F725ABE44000AEA95F /* PiecesView.swift in Sources */, A758BB0725AC119C0092714E /* Actions.swift in Sources */, A716976D262BFF8C00156BD6 /* SettingsView.swift in Sources */, diff --git a/Shared/Actions.swift b/Shared/Actions.swift index 5a1566a..7926992 100644 --- a/Shared/Actions.swift +++ b/Shared/Actions.swift @@ -56,8 +56,12 @@ struct Actions { document.lastMove = nil } - func undoMove() { - guard engine.canUndoMove() else { + func canMove(to: Direction) -> Bool { + return engine.canMove(to: to); + } + + func move(to: Direction) { + guard engine.canMove(to: to) else { return } @@ -68,21 +72,8 @@ struct Actions { if engine.isAnalyzing() { engine.cancel() } - engine.undoMove() - document.pgn = document.engine.pgn() - } - } - - func redoMove() { - guard engine.canRedoMove() else { - return - } - - withAnimation { - document.selection = Selection.empty() - document.lastMove = nil - - engine.redoMove() + + engine.move(to: to) document.pgn = document.engine.pgn() } } diff --git a/Shared/ActionsToolbar.swift b/Shared/ActionsToolbar.swift index 19540f3..c64fd95 100644 --- a/Shared/ActionsToolbar.swift +++ b/Shared/ActionsToolbar.swift @@ -79,24 +79,6 @@ struct RotateBoard: View { } } -struct UndoMoveButton: View { - @Binding var document: ChessDocument - var body: some View { - Button(action: { Actions(document: $document).undoMove() }) { - Label("Undo Move", systemImage: "arrow.uturn.backward.square") - }.disabled(!document.engine.canUndoMove()) - } -} - -struct RedoMoveButton: View { - @Binding var document: ChessDocument - var body: some View { - Button(action: { Actions(document: $document).redoMove() }) { - Label("Redo Move", systemImage: "arrow.uturn.forward.square") - }.disabled(!document.engine.canRedoMove()) - } -} - struct CopyFENButton: View { @Binding var document: ChessDocument var body: some View { @@ -154,17 +136,9 @@ struct ActionsToolbar: ToolbarContent { AnalyzeBoard(document: $document) TrainButton(document: $document) - } - label: { - Label("Game", systemImage: "ellipsis.circle") - } - - Menu { - UndoMoveButton(document: $document) - RedoMoveButton(document: $document) Divider() - + RotateBoard(document: $document) Divider() diff --git a/Shared/Bridge/FEngine.h b/Shared/Bridge/FEngine.h index d362bde..5942bfe 100644 --- a/Shared/Bridge/FEngine.h +++ b/Shared/Bridge/FEngine.h @@ -15,6 +15,13 @@ typedef void(^FEngineSearchCallback)(FEngineInfo * _Nonnull info, BOOL completed); typedef void(^FEngineDidUpdateCallback)(); +typedef NS_ENUM(NSInteger, Direction){ + start = 0, + end, + backward, + forward +}; + /** This class is the interface between the C++ engine and the Objective-C/Swift world*/ @interface FEngine : NSObject @@ -49,11 +56,8 @@ typedef void(^FEngineDidUpdateCallback)(); - (NSArray* _Nonnull)movesAt:(NSUInteger)rank file:(NSUInteger)file; - (void)move:(NSUInteger)move; -- (BOOL)canUndoMove; -- (BOOL)canRedoMove; - -- (void)undoMove; -- (void)redoMove; +- (BOOL)canMoveTo:(Direction)direction; +- (void)moveTo:(Direction)direction; - (void)move:(NSString* _Nonnull)from to:(NSString* _Nonnull)to; diff --git a/Shared/Bridge/FEngine.mm b/Shared/Bridge/FEngine.mm index aee7ee5..875d44c 100644 --- a/Shared/Bridge/FEngine.mm +++ b/Shared/Bridge/FEngine.mm @@ -172,24 +172,26 @@ - (void)move:(NSString*)from to:(NSString*)to { [self fireUpdate:self.stateIndex]; } -- (BOOL)canUndoMove { - return engine.canUndoMove(); +- (ChessGame::Direction)gameDirection:(Direction)direction { + switch (direction) { + case start: + return ChessGame::Direction::start; + case end: + return ChessGame::Direction::end; + case backward: + return ChessGame::Direction::backward; + case forward: + return ChessGame::Direction::forward; + } } - -- (BOOL)canRedoMove { - return engine.canRedoMove(); +- (BOOL)canMoveTo:(Direction)direction { + return engine.game.canMoveTo([self gameDirection:direction]); } -- (void)undoMove { +- (void)moveTo:(Direction)direction { // TODO: handle the cancel with a callback when the cancel actually really happened [self cancel]; - engine.undoMove(); - [self fireUpdate:self.stateIndex]; -} - -- (void)redoMove { - [self cancel]; - engine.redoMove(); + engine.game.moveTo([self gameDirection:direction]); [self fireUpdate:self.stateIndex]; } diff --git a/Shared/Engine/Engine/ChessEngine.hpp b/Shared/Engine/Engine/ChessEngine.hpp index cc96477..1405790 100644 --- a/Shared/Engine/Engine/ChessEngine.hpp +++ b/Shared/Engine/Engine/ChessEngine.hpp @@ -90,23 +90,7 @@ class ChessEngine { void move(std::string from, std::string to) { game.move(from, to); } - - bool canUndoMove() { - return game.canUndoMove(); - } - - bool canRedoMove() { - return game.canRedoMove(); - } - - void undoMove() { - game.undoMove(); - } - - void redoMove() { - game.redoMove(); - } - + void stop() { iterativeSearch.stop(); } diff --git a/Shared/Engine/Engine/ChessGame.cpp b/Shared/Engine/Engine/ChessGame.cpp index e177548..48f03a4 100644 --- a/Shared/Engine/Engine/ChessGame.cpp +++ b/Shared/Engine/Engine/ChessGame.cpp @@ -130,23 +130,35 @@ void ChessGame::move(std::string from, std::string to) { } } -bool ChessGame::canUndoMove() { - return moveIndexes.moveCursor > 0; -} - -bool ChessGame::canRedoMove() { - return moveIndexes.moveCursor < moveIndexes.moves.size(); -} - -void ChessGame::undoMove() { - assert(canUndoMove()); - moveIndexes.moveCursor--; - replayMoves(); +bool ChessGame::canMoveTo(Direction direction) { + switch (direction) { + case Direction::start: + return moveIndexes.moveCursor > 0; + case Direction::end: + return moveIndexes.moveCursor < moveIndexes.moves.size(); + case Direction::backward: + return moveIndexes.moveCursor > 0; + case Direction::forward: + return moveIndexes.moveCursor < moveIndexes.moves.size(); + } } -void ChessGame::redoMove() { - assert(canRedoMove()); - moveIndexes.moveCursor++; +void ChessGame::moveTo(Direction direction) { + assert(canMoveTo(direction)); + switch (direction) { + case Direction::start: + moveIndexes.moveCursor = 0; + break; + case Direction::end: + moveIndexes.moveCursor = (int)moveIndexes.moves.size(); + break; + case Direction::backward: + moveIndexes.moveCursor--; + break; + case Direction::forward: + moveIndexes.moveCursor++; + break; + } replayMoves(); } diff --git a/Shared/Engine/Engine/ChessGame.hpp b/Shared/Engine/Engine/ChessGame.hpp index 3cac448..fc95998 100644 --- a/Shared/Engine/Engine/ChessGame.hpp +++ b/Shared/Engine/Engine/ChessGame.hpp @@ -183,11 +183,12 @@ class ChessGame { void move(Move move, std::string comment, bool replace); void move(std::string from, std::string to); - bool canUndoMove(); - bool canRedoMove(); + enum class Direction { + start, end, backward, forward + }; - void undoMove(); - void redoMove(); + bool canMoveTo(Direction direction); + void moveTo(Direction direction); std::string getState(); diff --git a/Shared/Views/AnalyzeActionsView.swift b/Shared/Views/AnalyzeActionsView.swift deleted file mode 100644 index bc85ec9..0000000 --- a/Shared/Views/AnalyzeActionsView.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// AnalyzeActionsView.swift -// BChess -// -// Created by Jean Bovet on 4/26/21. -// Copyright © 2021 Jean Bovet. All rights reserved. -// - -import SwiftUI - -struct AnalyzeActionsView: View { - - @Binding var document: ChessDocument - - var body: some View { - HStack() { - Button(action: { - Actions(document: $document).analyzeReset() - }) { - Image(systemName: "arrow.counterclockwise.circle.fill") - } - Button(action: { - Actions(document: $document).undoMove() - }) { - Image(systemName: "arrowtriangle.backward.fill") - } - Button(action: { - Actions(document: $document).redoMove() - }) { - Image(systemName: "arrowtriangle.forward.fill") - } - } - } -} - -struct AnalyzeActionsView_Previews: PreviewProvider { - static var previews: some View { - let doc = ChessDocument() - AnalyzeActionsView(document: .constant(doc)) - } -} diff --git a/Shared/Views/ContentView.swift b/Shared/Views/ContentView.swift index 9929ede..fe80347 100644 --- a/Shared/Views/ContentView.swift +++ b/Shared/Views/ContentView.swift @@ -38,7 +38,7 @@ struct ContentView: View { if (showInfo) { VStack(alignment: .leading) { - AnalyzeActionsView(document: $document) + NavigationView(document: $document) InformationView(document: document) } diff --git a/Shared/Views/InformationView.swift b/Shared/Views/InformationView.swift index b863e0b..c940426 100644 --- a/Shared/Views/InformationView.swift +++ b/Shared/Views/InformationView.swift @@ -68,7 +68,6 @@ struct InformationView: View { } List(document.moveNodes.moveNodes, children: \FullMoveItem.children) { item in FullMoveItemView(item: item, currentMoveUUID: document.engine.currentMoveNodeUUID()) - .font(.system(.body, design: .monospaced)) } Spacer() if document.mode.value == .play { diff --git a/Shared/Views/NavigationView.swift b/Shared/Views/NavigationView.swift new file mode 100644 index 0000000..5988384 --- /dev/null +++ b/Shared/Views/NavigationView.swift @@ -0,0 +1,57 @@ +// +// AnalyzeActionsView.swift +// BChess +// +// Created by Jean Bovet on 4/26/21. +// Copyright © 2021 Jean Bovet. All rights reserved. +// + +import SwiftUI + +struct NavigationView: View { + + @Binding var document: ChessDocument + + var body: some View { + HStack() { + if document.mode.value != .play { + Button(action: { + Actions(document: $document).analyzeReset() + }) { + Image(systemName: "arrow.counterclockwise.circle.fill") + } + } + + Button(action: { + Actions(document: $document).move(to: .start) + }) { + Image(systemName: "backward.end.fill") + }.disabled(!document.engine.canMove(to: .start)) + + Button(action: { + Actions(document: $document).move(to: .backward) + }) { + Image(systemName: "arrowtriangle.backward.fill") + }.disabled(!document.engine.canMove(to: .backward)) + + Button(action: { + Actions(document: $document).move(to: .forward) + }) { + Image(systemName: "arrowtriangle.forward.fill") + }.disabled(!document.engine.canMove(to: .forward)) + + Button(action: { + Actions(document: $document).move(to: .end) + }) { + Image(systemName: "forward.end.fill") + }.disabled(!document.engine.canMove(to: .end)) + } + } +} + +struct NavigationView_Previews: PreviewProvider { + static var previews: some View { + let doc = ChessDocument() + NavigationView(document: .constant(doc)) + } +}