first game in main 🎉

master
Adam BONAFOS 4 months ago
parent 1fde20ecd5
commit 57283d55e5

@ -5,6 +5,6 @@
location = "group:Model"> location = "group:Model">
</FileRef> </FileRef>
<FileRef <FileRef
location = "group:../Connect4_CLI/Connect4_CLI.xcodeproj"> location = "group:Connect4_CLI/Connect4_CLI.xcodeproj">
</FileRef> </FileRef>
</Workspace> </Workspace>

@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
90BAEDC82D34F75500C6B480 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90BAEDC72D34F75500C6B480 /* main.swift */; }; 90BAEDC82D34F75500C6B480 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90BAEDC72D34F75500C6B480 /* main.swift */; };
90C8428D2D50D02200DDFF08 /* Model in Frameworks */ = {isa = PBXBuildFile; productRef = 90C8428C2D50D02200DDFF08 /* Model */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
@ -25,7 +26,6 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
90BAEDC42D34F75500C6B480 /* Connect4_CLI */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Connect4_CLI; sourceTree = BUILT_PRODUCTS_DIR; }; 90BAEDC42D34F75500C6B480 /* Connect4_CLI */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Connect4_CLI; sourceTree = BUILT_PRODUCTS_DIR; };
90BAEDC72D34F75500C6B480 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; }; 90BAEDC72D34F75500C6B480 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
90BAEDCE2D34F78600C6B480 /* Model */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Model; path = ../Model; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -33,6 +33,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
90C8428D2D50D02200DDFF08 /* Model in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -42,9 +43,9 @@
90BAEDBB2D34F75500C6B480 = { 90BAEDBB2D34F75500C6B480 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
90BAEDCE2D34F78600C6B480 /* Model */,
90BAEDC62D34F75500C6B480 /* Connect4_CLI */, 90BAEDC62D34F75500C6B480 /* Connect4_CLI */,
90BAEDC52D34F75500C6B480 /* Products */, 90BAEDC52D34F75500C6B480 /* Products */,
90C8428B2D50D02200DDFF08 /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -64,6 +65,13 @@
path = Connect4_CLI; path = Connect4_CLI;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
90C8428B2D50D02200DDFF08 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -80,6 +88,9 @@
dependencies = ( dependencies = (
); );
name = Connect4_CLI; name = Connect4_CLI;
packageProductDependencies = (
90C8428C2D50D02200DDFF08 /* Model */,
);
productName = Connect4_CLI; productName = Connect4_CLI;
productReference = 90BAEDC42D34F75500C6B480 /* Connect4_CLI */; productReference = 90BAEDC42D34F75500C6B480 /* Connect4_CLI */;
productType = "com.apple.product-type.tool"; productType = "com.apple.product-type.tool";
@ -282,6 +293,13 @@
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCSwiftPackageProductDependency section */
90C8428C2D50D02200DDFF08 /* Model */ = {
isa = XCSwiftPackageProductDependency;
productName = Model;
};
/* End XCSwiftPackageProductDependency section */
}; };
rootObject = 90BAEDBC2D34F75500C6B480 /* Project object */; rootObject = 90BAEDBC2D34F75500C6B480 /* Project object */;
} }

@ -6,6 +6,30 @@
// //
import Foundation import Foundation
import Model
print("Hello, World!") print("Connect4")
func read(question: String) -> String {
print(question)
let answer = readLine()
if let answer {
return answer
} else {
return ""
}
}
var board = Board(rowsNb: 6, columnsNb: 7)
var rules = Connect4Rules(piecesToAlign: 4)
var player1: Player = HumanPlayer(rules: rules, color: .red, name: "player1", CLI: read)
var player2: Player = HumanPlayer(rules: rules, color: .yellow, name: "player2", CLI: read)
while rules.isGameOver(board: board).0 != true {
var move = player1.play()
while rules.possibleMoves(board: board).contains(move) != true {
print("Move invalide en choisir un autre")
move = player1.play()
}
board[move.row, move.column] = player1.color
}

@ -18,7 +18,7 @@ public struct Board {
grid = Array(repeating: Array(repeating: Token.empty, count: columnsNb), count: rowsNb) grid = Array(repeating: Array(repeating: Token.empty, count: columnsNb), count: rowsNb)
} }
subscript(row: Int, column: Int) -> Token { public subscript(row: Int, column: Int) -> Token {
get { get {
guard row >= 0 && row < rowNb && column >= 0 && column < columnNb else { guard row >= 0 && row < rowNb && column >= 0 && column < columnNb else {
fatalError("Out of board") fatalError("Out of board")

@ -96,14 +96,14 @@ public struct Connect4Rules : Rules {
return true return true
} }
func possibleMoves(board: Board) -> [(row: Int, column: Int)] { public func possibleMoves(board: Board) -> [Move] {
var possibleMoves: [(Int, Int)] = [] var possibleMoves: [Move] = []
for column in 0..<board.columnNb { for column in 0..<board.columnNb {
// Trouver la première case vide en partant du bas de la colonne // Trouver la première case vide en partant du bas de la colonne
for row in (0..<board.rowNb).reversed() { for row in (0..<board.rowNb) {
if board[row, column] == Token.empty { if board[row, column] == Token.empty {
possibleMoves.append((row, column)) // Ajouter la position (row, column) possibleMoves.append(Move(row: row, column: column)) // Ajouter la position (row, column)
break // Une fois trouvé, on passe à la colonne suivante break // Une fois trouvé, on passe à la colonne suivante
} }
} }

@ -0,0 +1,18 @@
//
// File.swift
//
//
// Created by Adam BONAFOS on 03/02/2025.
//
import Foundation
public struct Move: Equatable {
public let row: Int
public let column: Int
public init(row: Int, column: Int) {
self.row = row
self.column = column
}
}

@ -6,17 +6,17 @@
// //
import Foundation import Foundation
class HumanPlayer : Player { public class HumanPlayer : Player {
// Propriété CLI de type closure // Propriété CLI de type closure
var CLI: ((String) -> String)? private var CLI: ((String) -> String)?
// Initialisation avec la closure CLI // Initialisation avec la closure CLI
init(CLI: @escaping (String) -> String) { public init(rules: Rules, color: Token, name: String, CLI: @escaping (String) -> String) {
self.CLI = CLI self.CLI = CLI
super.init() super.init(rules: rules, color: color, name: name)
} }
override func play() -> (row: Int, column: Int) { public override func play() -> Move {
// Demande la colonne à l'utilisateur // Demande la colonne à l'utilisateur
let columnInput = CLI!("Entrez le numéro de la colonne : ") let columnInput = CLI!("Entrez le numéro de la colonne : ")
guard let column = Int(columnInput) else { guard let column = Int(columnInput) else {
@ -27,6 +27,6 @@ class HumanPlayer : Player {
guard let row = Int(rowInput) else { guard let row = Int(rowInput) else {
fatalError("Entrée invalide pour la ligne.") fatalError("Entrée invalide pour la ligne.")
} }
return (row, column) return Move(row: row, column: column)
} }
} }

@ -6,16 +6,16 @@
// //
import Foundation import Foundation
class Player { public class Player {
var name: String var name: String
var color: Token public let color: Token
var rules: Rules var rules: Rules
init(rules: Rules, color: Token, name: String){ public init(rules: Rules, color: Token, name: String){
self.name = name self.name = name
self.color = color self.color = color
self.rules = rules self.rules = rules
} }
func play() -> (row: Int, column: Int){ public func play() -> Move {
fatalError("Must override in subclass") fatalError("Must override in subclass")
} }
} }

@ -7,7 +7,7 @@
import Foundation import Foundation
class RandomPlayer : AIPlayer { class RandomPlayer : AIPlayer {
override func play() -> (row: Int, column: Int) { override func play() -> Move {
return (0, 0) return Move(row: 0, column: 0)
} }
} }

@ -6,9 +6,10 @@
// //
import Foundation import Foundation
protocol Rules {
public protocol Rules {
func add(board: inout Board, position pos: (row: Int, column: Int), token: Token) -> Bool func add(board: inout Board, position pos: (row: Int, column: Int), token: Token) -> Bool
func isGameOver(board: Board) -> (result: Bool, winner: Token) func isGameOver(board: Board) -> (result: Bool, winner: Token)
func possibleMoves(board: Board) -> [(row: Int, column: Int)] func possibleMoves(board: Board) -> [Move]
} }

@ -8,13 +8,13 @@
import Foundation import Foundation
public struct TicTacToeRules: Rules { public struct TicTacToeRules: Rules {
func possibleMoves(board: Board) -> [(row: Int, column: Int)] { public func possibleMoves(board: Board) -> [Move] {
var possibleMoves: [(Int, Int)] = [] var possibleMoves: [Move] = []
for column in 0..<board.columnNb { for column in 0..<board.columnNb {
for row in 0..<board.rowNb { for row in 0..<board.rowNb {
if board[row, column] == Token.empty { if board[row, column] == Token.empty {
possibleMoves.append((row, column)) // Ajouter la position (row, column) possibleMoves.append(Move(row: row, column: column)) // Ajouter la position (row, column)
} }
} }
} }
@ -22,7 +22,7 @@ public struct TicTacToeRules: Rules {
return possibleMoves return possibleMoves
} }
func add(board: inout Board, position pos: (row: Int, column: Int), token: Token) -> Bool { public func add(board: inout Board, position pos: (row: Int, column: Int), token: Token) -> Bool {
if board[pos.0, pos.1] == .empty { if board[pos.0, pos.1] == .empty {
board[pos.0, pos.1] = token board[pos.0, pos.1] = token
return true return true
@ -30,7 +30,7 @@ public struct TicTacToeRules: Rules {
return false return false
} }
func isGameOver(board: Board) -> (result: Bool, winner: Token) { public func isGameOver(board: Board) -> (result: Bool, winner: Token) {
// Need to be complete // Need to be complete
// TODO // TODO
return (false, Token.empty) return (false, Token.empty)

Loading…
Cancel
Save