diff --git a/Sources/Games/Begriffix.swift b/Sources/Games/Begriffix.swift index 73575c0..f7ac08b 100644 --- a/Sources/Games/Begriffix.swift +++ b/Sources/Games/Begriffix.swift @@ -4,7 +4,15 @@ import Utility public struct Begriffix: DyadicGame, Trackable { public typealias Word = [Unicode.Scalar] public typealias Board = BegriffixBoard - public typealias MinWordLength = (restricted: Int, liberal: Int) + public typealias MinWordLength = (first: Int, other: Int) + /// Modes of restriction for the writing direction + public enum DirectionRestrictionMode { + /// Starter is unrestricted, opponent must write orthogonally to starter + case variable + /// Starter must write horizontally, opponent must write vertically + case fixed + } + public typealias DirectionRestrictions = (first: DirectionRestrictionMode?, other: DirectionRestrictionMode?) public typealias Players = DyadicPlayers public typealias Status = GameStatus public typealias Notify = ((_ status: Status) -> Void)? @@ -27,14 +35,14 @@ public struct Begriffix: DyadicGame, Trackable { } } public static let name = "Begriffix" - /// How many times the starter and opponent have provided a move - public private(set) var moveCount: Int = 0 + /// The already inserted moves + public private(set) var moves: [Move] = [] public var turn: Int { - return moveCount/2 + return moves.count / 2 } /// The current player position public var player: Players.Position { - return moveCount % 2 == 0 ? .starter : .opponent + return moves.count % 2 == 0 ? .starter : .opponent } /// The players coordinator public var players: Players @@ -44,21 +52,21 @@ public struct Begriffix: DyadicGame, Trackable { public let vocabulary: Radix? /// The minimum word lengths for the two game phases public let minWordLength: MinWordLength - /// Indicates if opponent must write orthogonally to starter in the first turn - public let firstOrthogonal: Bool + /// The restrictions of writing direction in and after the first turn + public let directionRestrictions: DirectionRestrictions public var notify: Notify /// Initialize a new begriffix game public init( board: Board, players: Players, - minWordLength: MinWordLength = (restricted: 5, liberal: 4), - firstOrthogonal: Bool = true, + minWordLength: MinWordLength = (first: 5, other: 4), + directionRestrictions: DirectionRestrictions = (first: .variable, other: nil), vocabulary: Radix? = nil ) { self.board = board self.players = players self.minWordLength = minWordLength - self.firstOrthogonal = firstOrthogonal + self.directionRestrictions = directionRestrictions self.vocabulary = vocabulary } /// Play the game and pass notifications if a notify callback is set @@ -76,12 +84,22 @@ public struct Begriffix: DyadicGame, Trackable { /// Apply a move to the game public mutating func insert(_ move: Move) throws { try board.insert(move.word, at: move.place) - moveCount += 1 + moves.append(move) + } + /// The direction which can currently be used to write, nil if not restricted + public var dir: Direction? { + guard let mode = turn == 0 ? directionRestrictions.first : directionRestrictions.other else {return nil} + switch mode { + case .fixed: + return self.player == .starter ? .horizontal : .vertical + case .variable: + return self.player == .opponent ? moves.last?.place.direction.orthogonal : nil + } } /// Find every place where words with allowed direction and length could be written public func find() -> FlattenCollection<[[Place]]> { - let min = turn == 0 ? minWordLength.restricted : minWordLength.liberal - if moveCount == 1, firstOrthogonal, let dir = board.findBalance() { + let min = turn == 0 ? minWordLength.first : minWordLength.other + if let dir = self.dir { return (min...board.sideLength) .concurrentMap {self.board.find(direction: dir, count: $0)} .joined() diff --git a/Sources/HangmanCLI/Simulation.swift b/Sources/HangmanCLI/Simulation.swift index f291323..541b905 100644 --- a/Sources/HangmanCLI/Simulation.swift +++ b/Sources/HangmanCLI/Simulation.swift @@ -57,6 +57,17 @@ extension SGSimulation.Info { } } +extension Begriffix.DirectionRestrictionMode { + init?(condition: SGSimulation.Condition.DirectionRestrictions.Mode) { + switch condition { + case .none: return nil + case .fixed: self = .fixed + case .starter: self = .variable + default: return nil + } + } +} + extension Begriffix { init?(condition: SGSimulation.Condition) { guard let starter = try? Player(config: condition.starter) else {return nil} @@ -64,17 +75,17 @@ extension Begriffix { let vocabulary = try? condition.vocabulary.load() guard let board = BegriffixBoard(condition: condition) else {return nil} let players = DyadicPlayers(starter: starter.move, opponent: opponent.move) - guard condition.hasWordMinLength else { - self.init(board: board, players: players, firstOrthogonal: condition.firstOrthogonal, vocabulary: vocabulary) - return - } - let minWordLength = ( - restricted: Int(condition.wordMinLength.restricted), - liberal: Int(condition.wordMinLength.liberal) - ) + let minWordLength = condition.hasWordMinLength ? + (first: Int(condition.wordMinLength.first), other: Int(condition.wordMinLength.other)) : (first: 5, other: 4) + let directionRestrictions = condition.hasDirectionRestrictions ? + ( + first: Begriffix.DirectionRestrictionMode(condition: condition.directionRestrictions.first), + other: Begriffix.DirectionRestrictionMode(condition: condition.directionRestrictions.other) + ) : + (first: .variable, other: nil) self.init( board: board, players: players, - minWordLength: minWordLength, firstOrthogonal: condition.firstOrthogonal, vocabulary: vocabulary + minWordLength: minWordLength, directionRestrictions: directionRestrictions, vocabulary: vocabulary ) } } diff --git a/Sources/HangmanCLI/play.swift b/Sources/HangmanCLI/play.swift index 3f9adf1..08fae6d 100644 --- a/Sources/HangmanCLI/play.swift +++ b/Sources/HangmanCLI/play.swift @@ -70,7 +70,7 @@ let playCommand = Command( private func move(_ game: Begriffix) -> Begriffix.Move? { let start: Point = ask("Which position do you want to start writing from?") let direction: Direction - if game.moveCount == 1, let dir = game.board.findBalance() { + if let dir = game.dir { print("Direction is \(dir), because we are in turn 1 and you are opponent") direction = dir } else { diff --git a/Sources/HangmanCLI/proto b/Sources/HangmanCLI/proto index e5985fa..b28d0e9 160000 --- a/Sources/HangmanCLI/proto +++ b/Sources/HangmanCLI/proto @@ -1 +1 @@ -Subproject commit e5985fa256cf14f091bf5921becc21f66db945fb +Subproject commit b28d0e95e67aa132479e0bb5034f0c5c4d41eb73 diff --git a/Tests/GamesTests/GameTests.swift b/Tests/GamesTests/GameTests.swift index 68f66b4..461fde5 100644 --- a/Tests/GamesTests/GameTests.swift +++ b/Tests/GamesTests/GameTests.swift @@ -33,6 +33,7 @@ class BegriffixTests: XCTestCase { XCTAssertEqual(move.hits?.count, 40) try! game.insert(move) guard let move2 = game.players.opponent(game) else {return} + XCTAssertEqual(game.moves.count, 1) XCTAssertEqual(move2.hits?.count, 20) XCTAssertNotEqual(move.place.direction, move2.place.direction) try! game.insert(move2)