diff --git a/Src/CLT/CLT/main.swift b/Src/CLT/CLT/main.swift
index de52867..e03d772 100644
--- a/Src/CLT/CLT/main.swift
+++ b/Src/CLT/CLT/main.swift
@@ -1,10 +1,3 @@
-//
-// main.swift
-// CLT
-//
-// Created by BREUIL Yohann on 17/01/2023.
-//
-
import Foundation
import Model
@@ -21,17 +14,35 @@ func insertPiece(id : Int, column : Int, _ board : inout Board) {
print("Board \(column) is full")
case .unknown:
print("Unknown")
+ case .negativeOrOutOfBound:
+ print("Row or column must be posittive")
+ case .alreadyTake:
+ print("Column already take")
}
default:
print("Rien")
}
+ print(board)
}
-if var board = Board(withGrid: [[1,2,1], [2,1,nil], [nil,nil,nil]]){
+if var board = Board(withNbRows: 6, withNbColumns: 7) {
print(board)
insertPiece(id: 1, column: 0, &board)
insertPiece(id: 1, column: 0, &board)
insertPiece(id: 1, column: 0, &board)
- insertPiece(id: 1, column: 0, &board)
- insertPiece(id: 1, column: 0, &board)
+ insertPiece(id: 2, column: 3, &board)
+ insertPiece(id: 2, column: 1, &board)
+ insertPiece(id: 2, column: 0, &board)
+ insertPiece(id: 2, column: 0, &board)
+ insertPiece(id: 2, column: 0, &board)
+ insertPiece(id: 2, column: 0, &board)
+ insertPiece(id: 2, column: 0, &board)
+ insertPiece(id: 2, column: 0, &board)
+ insertPiece(id: 2, column: 0, &board)
+
+ _ = board.removePiece(column: 0)
+ _ = board.removePiece(column: 0)
+ _ = board.removePiece(column: 8)
+
+ print(board)
}
diff --git a/Src/Model/.swiftpm/xcode/xcshareddata/xcschemes/Model.xcscheme b/Src/Model/.swiftpm/xcode/xcshareddata/xcschemes/Model.xcscheme
new file mode 100644
index 0000000..94fa614
--- /dev/null
+++ b/Src/Model/.swiftpm/xcode/xcshareddata/xcschemes/Model.xcscheme
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Src/Model/Sources/Model/Board/Board.swift b/Src/Model/Sources/Model/Board/Board.swift
index 6c722fe..ac94a0f 100644
--- a/Src/Model/Sources/Model/Board/Board.swift
+++ b/Src/Model/Sources/Model/Board/Board.swift
@@ -17,9 +17,10 @@ public struct Board : CustomStringConvertible {
for row in grid.reversed(){
for cell in row {
- string.append("\(String(describing: Board.descriptionMapper[cell] ?? "-")) ")
+ string.append("|")
+ string.append("\(String(describing: Board.descriptionMapper[cell] ?? "-"))")
}
- string.append("\n")
+ string.append("|\n")
}
return string
@@ -95,11 +96,11 @@ public struct Board : CustomStringConvertible {
/// - Parameter column : The column to add the piece
/// - Returns: The result of the insertion in a `BoardResult`
private mutating func insertPiece(id : Int, row : Int, column : Int) -> BoardResult {
- guard row >= 0 && row < nbRows && column >= 0 && column < nbColumns else{
- return .failed(reason: .unknown)
+ guard row >= 0 && row < nbRows && column >= 0 && column < nbColumns else {
+ return .failed(reason: .negativeOrOutOfBound)
}
guard grid[row][column] == nil else{
- return .failed(reason: .unknown)
+ return .failed(reason: .alreadyTake)
}
grid[row][column] = id
return .ok
@@ -111,30 +112,35 @@ public struct Board : CustomStringConvertible {
/// - Parameter column : The column to add the piece
/// - Returns: The result of the insertion in a `BoardResult`
public mutating func insertPiece(id : Int, column:Int) -> BoardResult {
+ guard column >= 0 && column < nbColumns else {
+ return .failed(reason: .negativeOrOutOfBound)
+ }
+
for row in 0.. Bool {
- guard row >= 0 && row < nbRows && column >= 0 && column < nbColumns else{
+ public mutating func removePiece(column : Int) -> Bool {
+ guard column >= 0 && column < nbRows else {
return false
}
- guard grid[row][column] == id else {
- return false
+
+ for row in stride(from: nbRows - 1, through: 0, by: -1) {
+ if grid[row][column] != nil{
+ grid[row][column] = nil
+ return true
+ }
}
- grid[row][column] = nil
return true
}
}
diff --git a/Src/Model/Sources/Model/Board/BoardResult.swift b/Src/Model/Sources/Model/Board/BoardResult.swift
index c4eff1e..d45997c 100644
--- a/Src/Model/Sources/Model/Board/BoardResult.swift
+++ b/Src/Model/Sources/Model/Board/BoardResult.swift
@@ -3,7 +3,7 @@ import Foundation
/// The result of an action on board
///
/// - Author: Yohann BREUIL
-public enum BoardResult {
+public enum BoardResult : Equatable {
case unknow
case ok
case failed(reason : FailedResult)
@@ -14,6 +14,8 @@ public enum BoardResult {
/// - Author: Yohann BREUIL
public enum FailedResult {
case unknown
+ case negativeOrOutOfBound
+ case alreadyTake
case columnFull
case boardFull
}
diff --git a/Src/Model/Sources/Model/Game/Game.swift b/Src/Model/Sources/Model/Game/Game.swift
new file mode 100644
index 0000000..a3fd8da
--- /dev/null
+++ b/Src/Model/Sources/Model/Game/Game.swift
@@ -0,0 +1,27 @@
+import Foundation
+
+public class Game {
+ private var rules : Rules
+
+ private var board : Board?
+
+ private var players : [Player] = []
+
+ /// Initialize game with a rule
+ public init(rules: Rules) {
+ self.rules = rules
+ self.board = createBoard(rules: rules)
+ }
+
+ ///
+ public func createBoard(rules: Rules) -> Board? {
+ Board(withNbRows: type(of: rules).nbRows, withNbColumns: type(of: rules).nbColumns) ?? nil
+ }
+
+ ///
+ public func insertPiece(player: Player) {
+ var col = player.chooseColumn()
+
+ board?.insertPiece(id: player.id, column: col)
+ }
+}
diff --git a/Src/Model/Sources/Model/Player/AI.swift b/Src/Model/Sources/Model/Player/AI.swift
index a1840dc..7faf6d0 100644
--- a/Src/Model/Sources/Model/Player/AI.swift
+++ b/Src/Model/Sources/Model/Player/AI.swift
@@ -4,5 +4,15 @@ import Foundation
///
/// - Author: Yohann BREUIL
public class AI : Player {
+ /// Initialize the artificial intelligence
+ ///
+ /// Artificial intelligence nickname was "AI" by default
+ public init() {
+ super.init(nickname: "AI")
+ }
+ ///
+ public override func chooseColumn() -> Int {
+ return 1
+ }
}
diff --git a/Src/Model/Sources/Model/Player/Human.swift b/Src/Model/Sources/Model/Player/Human.swift
index d66d16e..c79314c 100644
--- a/Src/Model/Sources/Model/Player/Human.swift
+++ b/Src/Model/Sources/Model/Player/Human.swift
@@ -4,5 +4,28 @@ import Foundation
///
/// - Author: Yohann BREUIL
public class Human : Player {
+ private var scanner : () -> Int
+ /// Initialize human player with a nickname and a scanner method
+ ///
+ /// - Parameter nickname : nickname to add to player
+ /// - Parameter scanner : scanner method which return an integer
+ public init(nickname: String, scanner: @escaping () -> Int) {
+ self.scanner = scanner
+ super.init(nickname: nickname)
+ }
+
+ /// Initialize human player with a scanner method
+ ///
+ /// Human nickname was "Player" by default
+ ///
+ /// - Parameter scanner : scanner method which return an integer
+ public convenience init(scanner: @escaping () -> Int) {
+ self.init(nickname: "Player", scanner: scanner)
+ }
+
+ ///
+ public override func chooseColumn() -> Int {
+ return 1
+ }
}
diff --git a/Src/Model/Sources/Model/Player/Player.swift b/Src/Model/Sources/Model/Player/Player.swift
index cb99468..ebf6384 100644
--- a/Src/Model/Sources/Model/Player/Player.swift
+++ b/Src/Model/Sources/Model/Player/Player.swift
@@ -4,12 +4,18 @@ import Foundation
///
/// - Author: Yohann BREUIL
public class Player {
+ private static var idCounter : Int = 1
+
public var id : Int
public var nickname : String
- public init(id: Int, nickname: String) {
- self.id = id
+ public init(nickname: String) {
+ self.id = Player.idCounter + 1
self.nickname = nickname
}
+
+ public func chooseColumn() -> Int {
+ return 1
+ }
}
diff --git a/Src/Model/Sources/Model/Rules/ClassicRules.swift b/Src/Model/Sources/Model/Rules/ClassicRules.swift
index 421469b..3e9aff7 100644
--- a/Src/Model/Sources/Model/Rules/ClassicRules.swift
+++ b/Src/Model/Sources/Model/Rules/ClassicRules.swift
@@ -1,21 +1,23 @@
/// Cclassic rules of Connect 4 game
-public class ClassicRules {
+public class ClassicRules : Rules {
public static var nbRows: Int = 6
public static var nbColumns: Int = 7
public static var nbAlignedPieces: Int = 4
public static var nbTrials: Int = 3
+
+ public var winCoord : [(Int, Int)] = []
- public init() {}
+ public func createBoard() -> Board? {
+ Board(withNbRows: Self.nbRows, withNbColumns: Self.nbColumns) ?? nil
+ }
+
+ ///
+ public func isGameOver() -> (Bool, Int, GameResult) {
+ return (true, 2, .lose);
+ }
-// public func createBoard() -> Board {
-//
-// }
-//
-// public func isGameOver() -> (Bool, Int, GameResult) {
-// <#code#>
-// }
-//
-// public func getNextPlayer() -> Int {
-// <#code#>
-// }
+ ///
+ public func getNextPlayer(board: Board) -> Int {
+ return 9;
+ }
}
diff --git a/Src/Model/Sources/Model/Rules/Rules.swift b/Src/Model/Sources/Model/Rules/Rules.swift
index 77f3227..8ac6da3 100644
--- a/Src/Model/Sources/Model/Rules/Rules.swift
+++ b/Src/Model/Sources/Model/Rules/Rules.swift
@@ -17,9 +17,7 @@ public protocol Rules {
static var nbTrials : Int {get}
/// Create board
- ///
- /// - Returns:`Board` created from rows and columns
- func createBoard() -> Board
+ func createBoard() -> Board?
/// Defines if game is over
///
@@ -28,6 +26,10 @@ public protocol Rules {
/// Returns id of next palyer to play
///
+ /// The player who must play is the one who has the fewest tokens
+ ///
+ /// - Parameter board : board game
+ ///
/// - Returns: id of next palyer to play
- func getNextPlayer() -> Int
+ func getNextPlayer(board: Board) -> Int
}
diff --git a/Src/Model/Tests/ModelTests/Board/BoardTests.swift b/Src/Model/Tests/ModelTests/Board/BoardTests.swift
index 5f872ae..883af08 100644
--- a/Src/Model/Tests/ModelTests/Board/BoardTests.swift
+++ b/Src/Model/Tests/ModelTests/Board/BoardTests.swift
@@ -71,9 +71,46 @@ final class BoardTest: XCTestCase {
}
XCTAssertNotNil(board)
- //XCTAssertEqual(board?.insertPiece(id: idPlayer, column: column), boardResult)
+ XCTAssertEqual(board?.insertPiece(id: idPlayer, column: column), boardResult)
}
-
- expect(grid: [[1,nil], [2,nil]], idPlayer: 2, column: 0, boardResult: .ok, notNil: true)
+
+ let grid : [[Int?]] = Array.init(repeating: Array.init(repeating: nil, count: 3), count: 3)
+
+ expect(grid: grid, idPlayer: 2, column: 0, boardResult: .ok, notNil: true)
+ expect(grid: grid, idPlayer: 2, column: 0, boardResult: .ok, notNil: true)
+ expect(grid: grid, idPlayer: 2, column: 0, boardResult: .ok, notNil: true)
+ //expect(grid: grid, idPlayer: 2, column: 0, boardResult: .failed(reason: <#T##FailedResult#>), notNil: true)
+
+ }
+
+ func testRemovePiece() {
+ func expect(grid : [[Int?]]) {
+
+ }
+ }
+
+ func testDescriptionBoard() {
+ func expect(grid : [[Int?]], result : String) {
+ let board = Board(withGrid: grid)
+
+ XCTAssertEqual(board?.description, result)
+ }
+
+ expect(grid: [[1, 1], [1, 1]], result: "|X|X|\n|X|X|\n")
+ expect(grid: [[1, 2], [1, 1]], result: "|X|X|\n|X|O|\n")
+ expect(grid: [[nil, 1], [nil, 1]], result: "|-|X|\n|-|X|\n")
+ expect(grid: [[nil, nil], [nil, nil]], result: "|-|-|\n|-|-|\n")
+ }
+
+ func testFullBoard() {
+ func expect(grid : [[Int?]], isFull : Bool) {
+ let board = Board(withGrid: grid)
+
+ XCTAssertEqual(board?.isFull(), isFull)
+ }
+
+ expect(grid: [[1, 1, 2], [1, 1, 1], [2, 2, 1]], isFull: true)
+ expect(grid: [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]], isFull: false)
+ expect(grid: [[nil, 2, nil], [1, 1, nil], [2, 2, nil]], isFull: false)
}
}
diff --git a/Src/Model/Tests/ModelTests/Human/HumanTests.swift b/Src/Model/Tests/ModelTests/Human/HumanTests.swift
deleted file mode 100644
index f78ef31..0000000
--- a/Src/Model/Tests/ModelTests/Human/HumanTests.swift
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// HumanTests.swift
-//
-//
-// Created by BREUIL Yohann on 11/02/2023.
-//
-
-import XCTest
-
-final class HumanTests: XCTestCase {
-
- override func setUpWithError() throws {
- // Put setup code here. This method is called before the invocation of each test method in the class.
- }
-
- override func tearDownWithError() throws {
- // Put teardown code here. This method is called after the invocation of each test method in the class.
- }
-
- func testExample() throws {
- // This is an example of a functional test case.
- // Use XCTAssert and related functions to verify your tests produce the correct results.
- // Any test you write for XCTest can be annotated as throws and async.
- // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
- // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
- }
-
- func testPerformanceExample() throws {
- // This is an example of a performance test case.
- self.measure {
- // Put the code you want to measure the time of here.
- }
- }
-
-}
diff --git a/Src/Model/Tests/ModelTests/Player/AITests.swift b/Src/Model/Tests/ModelTests/Player/AITests.swift
new file mode 100644
index 0000000..805cb8c
--- /dev/null
+++ b/Src/Model/Tests/ModelTests/Player/AITests.swift
@@ -0,0 +1,17 @@
+import XCTest
+import Model
+
+final class AITests: XCTestCase {
+ func testInit() {
+ func expect(nickname: String) {
+ let ai = AI()
+
+ XCTAssertEqual(ai.nickname, nickname)
+ }
+
+ expect(nickname: "yobreuil")
+ expect(nickname: "cebouhou")
+ expect(nickname: "macheval")
+ }
+
+}
diff --git a/Src/Model/Tests/ModelTests/Player/HumanTests.swift b/Src/Model/Tests/ModelTests/Player/HumanTests.swift
new file mode 100644
index 0000000..e6557eb
--- /dev/null
+++ b/Src/Model/Tests/ModelTests/Player/HumanTests.swift
@@ -0,0 +1,18 @@
+import XCTest
+import Model
+
+final class HumanTests: XCTestCase {
+ func testInit() {
+ func expect(nickname : String) {
+ func scan() -> Int { return 1 }
+
+ let human = Human(nickname: nickname, scanner: scan)
+
+ XCTAssertEqual(human.nickname, nickname)
+ }
+
+ expect(nickname: "yobreuil")
+ expect(nickname: "cebouhou")
+ expect(nickname: "macheval")
+ }
+}
diff --git a/Src/Model/Tests/ModelTests/Rules/ClassicRulesTests.swift b/Src/Model/Tests/ModelTests/Rules/ClassicRulesTests.swift
new file mode 100644
index 0000000..e21a138
--- /dev/null
+++ b/Src/Model/Tests/ModelTests/Rules/ClassicRulesTests.swift
@@ -0,0 +1,12 @@
+import XCTest
+import Model
+
+final class ClassicRulesTests: XCTestCase {
+ func testStaticValues() {
+ XCTAssertEqual(6, ClassicRules.nbRows)
+ XCTAssertEqual(7, ClassicRules.nbColumns)
+ XCTAssertEqual(4, ClassicRules.nbAlignedPieces)
+ XCTAssertEqual(3, ClassicRules.nbTrials)
+ }
+
+}