Check, fix, check ser-de

Persistance
Mathieu GROUSSEAU 2 months ago
parent 8770d1f423
commit 2f7ff9bbdc

@ -31,11 +31,7 @@ enum PlayerType: String, CaseIterable {
case Human = "Player" case Human = "Player"
} }
func createPlayer(type: PlayerType, playing pieces: PieceType, named name: String) -> Player { func playerPrompt(allowed_moves: [Move.Action], board: Board) -> Move.Action {
switch type {
case .Human:
HumanPlayer(name: name, piece_type: pieces, callback: {
allowed_moves, board in
for (i, action) in allowed_moves.enumerated() { for (i, action) in allowed_moves.enumerated() {
let text = switch action { let text = switch action {
case .InsertAt(let at): "Place piece at \(at.col):\(at.row)" case .InsertAt(let at): "Place piece at \(at.col):\(at.row)"
@ -51,7 +47,12 @@ func createPlayer(type: PlayerType, playing pieces: PieceType, named name: Strin
} }
return allowed_moves[index - 1] return allowed_moves[index - 1]
}) }
func createPlayer(type: PlayerType, playing pieces: PieceType, named name: String) -> Player {
switch type {
case .Human:
HumanPlayer(name: name, piece_type: pieces, callback: playerPrompt)
case .AIRandom: case .AIRandom:
RandomPlayer(name: name, piece_type: pieces) RandomPlayer(name: name, piece_type: pieces)
} }
@ -68,7 +69,7 @@ let players: [Player] = PieceType.allCases.map { type in
return createPlayer(type: ptype, playing: type, named: playerName) return createPlayer(type: ptype, playing: type, named: playerName)
} }
let rules: Rules = switch gameType { let rules: any Rules = switch gameType {
case .FourInARow: FourInARowRules(players: players)! case .FourInARow: FourInARowRules(players: players)!
case .TicTacToe: TicTacToeRules(players: players)! case .TicTacToe: TicTacToeRules(players: players)!
} }
@ -104,3 +105,9 @@ encoder.userInfo[.gameDecodingContext] = CodableContext()
let data = try encoder.encode(CodableGameWrapper(of: game)) let data = try encoder.encode(CodableGameWrapper(of: game))
print(String(data: data, encoding: .utf8)!) print(String(data: data, encoding: .utf8)!)
var decoder = JSONDecoder()
decoder.userInfo[.gameDecodingContext] = CodableContext(creatingCallbacks: { name, type in playerPrompt })
let game2 = (try decoder.decode(CodableGameWrapper.self, from: data)).game
assert(game == game2, "Games are not equals!")

@ -42,10 +42,13 @@ public struct Board: Equatable {
} }
} }
/// Iterate in the same order as coords
public var cells: any Sequence<Piece?> { public var cells: any Sequence<Piece?> {
self.grid.lazy.flatMap { (row: [Piece?]) -> LazySequence<[Piece?]> in row.lazy } // self.grid.lazy.flatMap { (row: [Piece?]) -> LazySequence<[Piece?]> in row.lazy }
self.coords.map { self[$0] }
} }
/// Iterate row by row, from 0 to width
public var coords: any Sequence<Coords> { public var coords: any Sequence<Coords> {
(0..<self.rows).lazy.flatMap { (row: Int) in (0..<self.rows).lazy.flatMap { (row: Int) in
(0..<self.columns).lazy.map { (column: Int) in (0..<self.columns).lazy.map { (column: Int) in

@ -1,20 +1,20 @@
public typealias BoardChangedListener = (Board) -> Void public typealias BoardChangedListener = (Board) -> Void
public typealias GameStateChangedListener = (GameState) -> Void public typealias GameStateChangedListener = (GameState) -> Void
public class Game { public class Game: Equatable {
public var boardChanged = Event<Board>() public var boardChanged = Event<Board>()
public var gameStateChanged = Event<GameState>() public var gameStateChanged = Event<GameState>()
public var moveIsInvalid = Event<Move>() public var moveIsInvalid = Event<Move>()
public let players: [Player] public let players: [Player]
public let rules: Rules public let rules: any Rules
/// Has to be public for serialization purposes /// Has to be public for serialization purposes
public var board: Board public var board: Board
/// Has to be public for serialization purposes /// Has to be public for serialization purposes
public var state: GameState public var state: GameState
public init(players: [Player], rules: Rules) { public init(players: [Player], rules: any Rules) {
self.players = players self.players = players
self.rules = rules self.rules = rules
@ -23,7 +23,7 @@ public class Game {
self.state = rules.gameState(board: board, last_turn: nil) self.state = rules.gameState(board: board, last_turn: nil)
} }
public init?(players: [Player], rules: Rules, board: Board, state: GameState?) { public init?(players: [Player], rules: any Rules, board: Board, state: GameState?) {
guard rules.isValid(board: board) else { return nil } guard rules.isValid(board: board) else { return nil }
self.state = state ?? rules.gameState(board: board, last_turn: nil) self.state = state ?? rules.gameState(board: board, last_turn: nil)
@ -96,6 +96,13 @@ public class Game {
board[finalCoords] = Piece(owner: move.player) board[finalCoords] = Piece(owner: move.player)
} }
public static func == (lhs: Game, rhs: Game) -> Bool {
lhs.players == rhs.players
&& RulesUtils.equals(lhs: lhs.rules, rhs: rhs.rules)
&& lhs.board == rhs.board
&& lhs.state == rhs.state
}
} }
@dynamicCallable @dynamicCallable

@ -19,8 +19,7 @@ public class Player : Equatable {
} }
public static func == (lhs: Player, rhs: Player) -> Bool { public static func == (lhs: Player, rhs: Player) -> Bool {
// TODO: name equality or reference equality? lhs === rhs || (lhs.type == rhs.type && lhs.name == rhs.name && lhs.piece_type == rhs.piece_type)
lhs === rhs
} }
} }

@ -1,4 +1,4 @@
public protocol Rules { public protocol Rules: Equatable {
var type: RulesTypes { get } var type: RulesTypes { get }
func createBoard() -> Board func createBoard() -> Board
@ -14,6 +14,22 @@ public protocol Rules {
func gameState(board: Board, last_turn: Player?) -> GameState func gameState(board: Board, last_turn: Player?) -> GameState
} }
public struct RulesUtils {
public static func equals(lhs: any Rules, rhs: any Rules) -> Bool {
let t1 = lhs.type
let t2 = rhs.type
if t1 != t2 { return false }
return switch t1 {
case .FourInARow:
(lhs as! FourInARowRules) == (rhs as! FourInARowRules)
case .TicTacToe:
(lhs as! TicTacToeRules) == (rhs as! TicTacToeRules)
}
}
}
public enum GameState: Equatable { public enum GameState: Equatable {
case Playing(turn: Player) case Playing(turn: Player)
case Win(winner: Player, board: Board, cells: [Coords]) case Win(winner: Player, board: Board, cells: [Coords])

@ -1,9 +1,9 @@
import Model import Model
public struct CodableRulesWrapper: Codable { public struct CodableRulesWrapper: Codable {
public let rules: Rules public let rules: any Rules
public init(of rules: Rules) { public init(of rules: any Rules) {
self.rules = rules self.rules = rules
} }
@ -30,7 +30,7 @@ extension RulesTypes: CodingKey, Codable {
try CodableEnum(of: self).encode(to: encoder) try CodableEnum(of: self).encode(to: encoder)
} }
public func decodeRules(from decoder: any Decoder) throws -> Rules { public func decodeRules(from decoder: any Decoder) throws -> any Rules {
switch self { switch self {
case .FourInARow: try FourInARowRules(from: decoder) case .FourInARow: try FourInARowRules(from: decoder)
case .TicTacToe: try TicTacToeRules(from: decoder) case .TicTacToe: try TicTacToeRules(from: decoder)

Loading…
Cancel
Save