winner's pieces visualization
continuous-integration/drone/push Build is passing Details

main
Mathis RIBEMONT 2 years ago
parent a1bd7bb178
commit bc332e8e19

BIN
.DS_Store vendored

Binary file not shown.

@ -40,7 +40,18 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "model"
BuildableName = "model"
BlueprintName = "model"
ReferencedContainer = "container:">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">

@ -7,7 +7,7 @@
import Foundation
public class Afficheur {
public class Afficheur : IAfficheur {
public init() {
}

@ -7,7 +7,7 @@
import Foundation
public class Lecteur {
public class Lecteur : ILecteur {
public init() {
}

@ -11,7 +11,9 @@ public struct Board: CustomStringConvertible {
var grid: [[Int?]];
let nbRows: Int;
let nbColumns: Int;
private static let descriptionMapper: [Int?:String] = [nil:" ", 1:"O", 2:"X"];
private var winCoord: Array<(x: Int, y: Int)>?
private static let descriptionMapper: [Int?:String] = [nil:"", 1:"🔴", 2:"🟡"];
private static let winnerDescriptionMapper = "🟢"
public init?(nbR: Int = 6, nbC: Int = 7) {
if nbR <= 0 || nbC <= 0 {
@ -42,31 +44,36 @@ public struct Board: CustomStringConvertible {
public var description: String {
var string = String()
for row in grid {
string.append("| ")
for cell in row {
string.append("\(String(describing: Board.descriptionMapper[cell] ?? " "))")
string.append(" | ")
for row in 0..<nbRows {
string.append("|")
for column in 0..<nbColumns {
if winCoord != nil && winCoord!.contains(where: { $0.x == column && $0.y == row } ) {
string.append("\(Board.winnerDescriptionMapper)")
} else {
let cell = grid[row][column]
string.append("\(String(describing: Board.descriptionMapper[cell] ?? " "))")
}
string.append("|")
}
string.append("\n")
}
string.append("|")
/*string.append("|")
for _ in 0...grid.count{
string.append("---|")
string.append("--|")
}
string.append("\n")
string.append("\n")*/
string.append("|")
string.append(" ")
for i in 0...grid.count{
string.append(" \(i) |")
string.append(" \(i) ")
}
return string
}
public func isFull() -> Bool {
for column in 0...nbColumns{
for column in 0..<nbColumns{
if !isColumnFull(column: column) {
return false
}
@ -128,4 +135,8 @@ public struct Board: CustomStringConvertible {
}
}
}
public mutating func submitWinCoord(_ winCoord: [(x: Int, y: Int)]) {
self.winCoord = winCoord
}
}

@ -10,45 +10,17 @@ import Foundation
public struct Game {
private var board: Board
private var players: [Player]
private var numero: Int = 0
private var afficheur: Afficheur
private(set) var numero: Int = 0
private var afficheur: IAfficheur
private var rule: Rule
public init?( withBoard board: inout Board, playedBy players: [Player], withRules rule: Rule, writeOn afficheur: Afficheur) {
public init(withBoard board: inout Board, playedBy players: [Player], withRules rule: Rule, writeOn afficheur: IAfficheur) {
self.board = board
self.players = players
self.afficheur = afficheur
self.rule = rule
}
public init?(withGrid grid: [[Int?]], playedBy players: [Player], withRules rule: Rule, writeOn afficheur: Afficheur) {
let b = Board(withGrid: grid)
guard b != nil else {
return nil
}
board = b!
self.players = players
self.afficheur = afficheur
self.rule = rule
}
public init?(withNbRow nbRow: Int = 6, withNbCol nbCol: Int = 7, playedBy players: [Player], withRules rule: Rule, writeOn afficheur: Afficheur) {
let b = Board(nbR: nbRow, nbC: nbCol)
guard b != nil else {
return nil
}
board = b!
self.players = players
self.afficheur = afficheur
self.rule = rule;
}
public mutating func tour() -> Player?{
let player = players[numero]
var result = BoardResult.unknown

@ -9,10 +9,10 @@ import Foundation
public class Human : Player {
public var name: String
private var lecteur: Lecteur
private var lecteur: ILecteur
// au lieu du protocol Lecteur, prendre une fonction qui sera appelé à chaque fois si on ne veux qu'une seul méthode (c'est une closure, si on la stock, on doit mettre @escaping en paramètre). On peut stocker une méthode optionnel
public init(named name: String, readOn lecteur: Lecteur){
public init(named name: String, readOn lecteur: ILecteur){
self.name = name
self.lecteur = lecteur
}

@ -0,0 +1,12 @@
//
// File.swift
//
//
// Created by etudiant on 10/02/2023.
//
import Foundation
public protocol IAfficheur {
func afficherLigne(message msg: String)
}

@ -0,0 +1,13 @@
//
// File.swift
//
//
// Created by etudiant on 10/02/2023.
//
import Foundation
public protocol ILecteur {
func lireInt() -> Int
func lireLigne() -> String?
}

@ -8,7 +8,7 @@
import Foundation
public protocol Player {
var name: String { get set }
var name: String { get }
func playInColumn() -> Int
}

@ -24,9 +24,12 @@ public class ClassicRules : Rule {
public func execute(column: Int, board: inout Board) -> Int? {
let grid = board.grid
var result = getLastPiece(column: column, board: &board)
var y = result.y
var id = result.id
let result = getLastPiece(column: column, board: &board)
let y = result.y
let id = result.id
if id == 0 { return nil }
var winCoord = [(x: Int, y: Int)]()
var score = 0
@ -52,24 +55,36 @@ public class ClassicRules : Rule {
for i in minX...maxX {
if grid[y][i] == id {
score += 1
winCoord += [(x: i, y: y)]
if score >= 4 {
board.submitWinCoord(winCoord)
return id
}
} else {
score = 0
}
}
if score >= 4 { return id }
winCoord = [(x: Int, y: Int)]()
//colonne
score = 0
for i in minY...maxY {
if grid[i][column] == id {
score += 1
winCoord += [(x: column, y: i)]
if score >= 4 {
board.submitWinCoord(winCoord)
return id
}
} else {
score = 0
}
}
if score >= 4 { return id }
winCoord = [(x: Int, y: Int)]()
//diagonale NO -> SE
score = 0
var minOffsetNOSE = -3
if column + minOffsetNOSE < 0 {
minOffsetNOSE = 0 - column
@ -90,14 +105,20 @@ public class ClassicRules : Rule {
//print("\(y+i),\(column+i)\n")
if grid[y+i][column+i] == id {
score += 1
winCoord += [(x: column+i, y: y+i)]
if score >= 4 {
board.submitWinCoord(winCoord)
return id
}
} else {
score = 0
}
}
if score >= 4 { return id }
winCoord = [(x: Int, y: Int)]()
//diagonale SO -> NE
score = 0
var c = column + y
var minOffsetSONE = -3
@ -118,14 +139,18 @@ public class ClassicRules : Rule {
}
//print("min: \(minOffsetSONE) max: \(maxOffsetSONE)")
for i in minOffsetSONE...maxOffsetSONE {
//print("\(column+i),\(y-i)\n")
//print("\(column+i),\(y-i)")
if grid[y-i][column+i] == id {
score += 1
winCoord += [(x: column+i, y: y-i)]
if score >= 4 {
board.submitWinCoord(winCoord)
return id
}
} else {
score = 0
}
}
if score >= 4 { return id }
return nil
}

@ -54,4 +54,24 @@ final class boardTests: XCTestCase {
}
}
}
func test_isFull(){
var board = Board()!
XCTAssertFalse(board.isFull())
for i in 0..<board.nbColumns {
for j in 0..<board.nbRows {
board.insertPiece(id: 1, column: i)
if i == board.nbColumns - 1 && j == board.nbRows - 1 {
XCTAssertTrue(board.isFull())
} else {
XCTAssertFalse(board.isFull())
}
}
}
}
func test_description() {
let board = Board()!
XCTAssertNotNil(board.description)
}
}

@ -3,6 +3,121 @@ import XCTest
final class ClassicRulesTests: XCTestCase {
public func test_execute(){
let rule = ClassicRules()
let id = Int.random(in: 1...2)
var array = newClassicGrid()
XCTAssertNil(execute(column: 0, grid: array))
array[3][3] = id
XCTAssertNil(execute(column: 3, grid: array))
//ligne
array[3][3] = id
XCTAssertEqual(nil, execute(column: 3, grid: array))
array[3][5] = id
XCTAssertEqual(nil, execute(column: 5, grid: array))
array[3][6] = id
XCTAssertEqual(nil, execute(column: 6, grid: array))
array[3][4] = id
XCTAssertEqual(id, execute(column: 4, grid: array))
XCTAssertNil(execute(column: 0, grid: array))
XCTAssertNil(execute(column: 1, grid: array))
XCTAssertNil(execute(column: 2, grid: array))
array = newClassicGrid()
//colonne
array[2][3] = id
XCTAssertEqual(nil, execute(column: 3, grid: array))
array[5][3] = id
XCTAssertEqual(nil, execute(column: 3, grid: array))
array[4][3] = id
XCTAssertEqual(nil, execute(column: 3, grid: array))
array[3][3] = id
XCTAssertEqual(id, execute(column: 3, grid: array))
XCTAssertNil(execute(column: 0, grid: array))
XCTAssertNil(execute(column: 1, grid: array))
XCTAssertNil(execute(column: 2, grid: array))
XCTAssertNil(execute(column: 4, grid: array))
XCTAssertNil(execute(column: 5, grid: array))
XCTAssertNil(execute(column: 6, grid: array))
array = newClassicGrid()
// Diagonale NOSE gauche
array[5][5] = id
XCTAssertEqual(nil, execute(column: 5, grid: array))
array[4][4] = id
XCTAssertEqual(nil, execute(column: 4, grid: array))
array[3][3] = id
XCTAssertEqual(nil, execute(column: 3, grid: array))
array[2][2] = id
XCTAssertEqual(id, execute(column: 2, grid: array))
XCTAssertNil(execute(column: 0, grid: array))
XCTAssertNil(execute(column: 1, grid: array))
XCTAssertNil(execute(column: 6, grid: array))
array = newClassicGrid()
// Diagonale NOSE gauche
array[4][4] = id
XCTAssertEqual(nil, execute(column: 4, grid: array))
array[3][3] = id
XCTAssertEqual(nil, execute(column: 3, grid: array))
array[2][2] = id
XCTAssertEqual(nil, execute(column: 2, grid: array))
array[5][5] = id
XCTAssertEqual(id, execute(column: 5, grid: array))
XCTAssertNil(execute(column: 0, grid: array))
XCTAssertNil(execute(column: 1, grid: array))
XCTAssertNil(execute(column: 6, grid: array))
array = newClassicGrid()
// Diagonale SONE gauche
array[5][2] = id
XCTAssertEqual(nil, execute(column: 2, grid: array))
array[3][4] = id
XCTAssertEqual(nil, execute(column: 4, grid: array))
array[4][3] = id
XCTAssertEqual(nil, execute(column: 3, grid: array))
array[2][5] = id
XCTAssertEqual(id, execute(column: 5, grid: array))
XCTAssertNil(execute(column: 0, grid: array))
XCTAssertNil(execute(column: 1, grid: array))
XCTAssertNil(execute(column: 6, grid: array))
array = newClassicGrid()
// Diagonale SONE droite
array[2][5] = id
XCTAssertEqual(nil, execute(column: 5, grid: array))
array[3][4] = id
XCTAssertEqual(nil, execute(column: 4, grid: array))
array[4][3] = id
XCTAssertEqual(nil, execute(column: 3, grid: array))
array[5][2] = id
XCTAssertEqual(id, execute(column: 2, grid: array))
XCTAssertNil(execute(column: 0, grid: array))
XCTAssertNil(execute(column: 1, grid: array))
XCTAssertNil(execute(column: 6, grid: array))
array = newClassicGrid()
}
private func execute(column: Int, grid: [[Int?]]) -> Int?{
var board = Board(withGrid: grid)!
let rule = ClassicRules(withBoard: &board)!
return rule.execute(column: column, board: &board)
}
private func newClassicGrid() -> [[Int?]] {
return Array(repeating: Array(repeating: nil, count: 7), count: 6)
}
}

@ -2,11 +2,27 @@ import XCTest
@testable import model
public class GameTests : XCTestCase {
func test_constructeur(){
//var game = Game(withNbRow: 4, withNbCol: 4, playedBy: [Player(named: "Joueur1"), Player(named: "Joueur2")])
//XCTAssertNotNil(game)
func test_constructor() {
var board = Board()!
let player1 = IA(named: "Lya", playedOn: &board)
let player2 = IA(named: "Charle", playedOn: &board)
let rule = ClassicRules(withBoard: &board)!
let afficheur = Afficheur()
//game = Game(withGrid: [[nil,1],[1,2]], playedBy: [Player(named: "Joueur1"), Player(named: "Joueur2")])
//XCTAssertNotNil(game)
XCTAssertNotNil(Game(withBoard: &board, playedBy: [player1, player2], withRules: rule, writeOn: afficheur))
}
func test_joueurSuivant() {
var board = Board()!
let player1 = IA(named: "Lya", playedOn: &board)
let player2 = IA(named: "Charle", playedOn: &board)
let rule = ClassicRules(withBoard: &board)!
let afficheur = Afficheur()
var game = Game(withBoard: &board, playedBy: [player1, player2], withRules: rule, writeOn: afficheur)
XCTAssertEqual(0, game.numero)
game.joueurSuivant()
XCTAssertEqual(1, game.numero)
}
}

@ -0,0 +1,11 @@
import XCTest
@testable import model
final class HumanTests: XCTestCase {
func test_construct() {
let name = "charle"
let player = Human(named: name, readOn: Lecteur())
XCTAssertEqual(name, player.name)
}
}

@ -0,0 +1,19 @@
import XCTest
@testable import model
final class IATests: XCTestCase {
func test_ia() {
let strength = 1000
var board = Board()!
let name = "Lya"
let ia = IA(named: name, playedOn: &board)
XCTAssertEqual(name, ia.name)
for _ in 0..<strength {
let n = ia.playInColumn()
XCTAssertTrue(n >= 0)
XCTAssertTrue(n < board.nbColumns)
}
}
}

@ -14,8 +14,8 @@
filePath = "model/Sources/model/rules/ClassicRules.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "136"
endingLineNumber = "136"
startingLineNumber = "161"
endingLineNumber = "161"
landmarkName = "getLastPiece(column:board:)"
landmarkType = "7">
</BreakpointContent>
@ -30,8 +30,8 @@
filePath = "model/Sources/model/rules/ClassicRules.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "137"
endingLineNumber = "137"
startingLineNumber = "162"
endingLineNumber = "162"
landmarkName = "getLastPiece(column:board:)"
landmarkType = "7">
</BreakpointContent>
@ -46,8 +46,8 @@
filePath = "model/Sources/model/model/Game.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "53"
endingLineNumber = "53"
startingLineNumber = "25"
endingLineNumber = "25"
landmarkName = "tour()"
landmarkType = "7">
</BreakpointContent>
@ -94,8 +94,8 @@
filePath = "model/Sources/model/model/Game.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "77"
endingLineNumber = "77"
startingLineNumber = "49"
endingLineNumber = "49"
landmarkName = "tour()"
landmarkType = "7">
</BreakpointContent>
@ -110,8 +110,8 @@
filePath = "model/Sources/model/rules/ClassicRules.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "31"
endingLineNumber = "31"
startingLineNumber = "34"
endingLineNumber = "34"
landmarkName = "execute(column:board:)"
landmarkType = "7">
</BreakpointContent>
@ -126,8 +126,8 @@
filePath = "model/Sources/model/rules/ClassicRules.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "134"
endingLineNumber = "134"
startingLineNumber = "159"
endingLineNumber = "159"
landmarkName = "getLastPiece(column:board:)"
landmarkType = "7">
</BreakpointContent>
@ -142,8 +142,8 @@
filePath = "model/Sources/model/rules/ClassicRules.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "88"
endingLineNumber = "88"
startingLineNumber = "103"
endingLineNumber = "103"
landmarkName = "execute(column:board:)"
landmarkType = "7">
</BreakpointContent>
@ -158,8 +158,8 @@
filePath = "model/Sources/model/rules/ClassicRules.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "73"
endingLineNumber = "73"
startingLineNumber = "88"
endingLineNumber = "88"
landmarkName = "execute(column:board:)"
landmarkType = "7">
</BreakpointContent>
@ -174,11 +174,138 @@
filePath = "model/Sources/model/rules/ClassicRules.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "101"
endingLineNumber = "101"
startingLineNumber = "122"
endingLineNumber = "122"
landmarkName = "execute(column:board:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "2672758A-EED9-4C54-A1F6-CEFCAF10A7E2"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "model/Tests/modelTests/BoardTests.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "59"
endingLineNumber = "59"
landmarkName = "test_isFull()"
landmarkType = "7">
<Locations>
<Location
uuid = "2672758A-EED9-4C54-A1F6-CEFCAF10A7E2 - 2c4327bd6856b6e0"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "modelTests.boardTests.test_isColumnFull() -&gt; ()"
moduleName = "modelTests"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/etudiant/Documents/puissance4/model/Tests/modelTests/BoardTests.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "59"
endingLineNumber = "59"
offsetFromSymbolStart = "220">
</Location>
<Location
uuid = "2672758A-EED9-4C54-A1F6-CEFCAF10A7E2 - 2c4327bd6856b6e0"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "modelTests.boardTests.test_isColumnFull() -&gt; ()"
moduleName = "modelTests"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/etudiant/Documents/puissance4/model/Tests/modelTests/BoardTests.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "59"
endingLineNumber = "59"
offsetFromSymbolStart = "232">
</Location>
<Location
uuid = "2672758A-EED9-4C54-A1F6-CEFCAF10A7E2 - 2c4327bd6856b6e0"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "modelTests.boardTests.test_isColumnFull() -&gt; ()"
moduleName = "modelTests"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/etudiant/Documents/puissance4/model/Tests/modelTests/BoardTests.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "59"
endingLineNumber = "59"
offsetFromSymbolStart = "244">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "65361A5B-DAF8-47E9-B2C0-71B5868FE141"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "model/Tests/modelTests/BoardTests.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "62"
endingLineNumber = "62"
landmarkName = "test_isFull()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "8B55A247-FEBB-46DC-B681-4EFF804883AF"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "model/Tests/modelTests/BoardTests.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "65"
endingLineNumber = "65"
landmarkName = "test_isFull()"
landmarkType = "7">
<Locations>
<Location
uuid = "8B55A247-FEBB-46DC-B681-4EFF804883AF - 2c4327bd6856b79e"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "modelTests.boardTests.test_isColumnFull() -&gt; ()"
moduleName = "modelTests"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/etudiant/Documents/puissance4/model/Tests/modelTests/BoardTests.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "65"
endingLineNumber = "65"
offsetFromSymbolStart = "1872">
</Location>
<Location
uuid = "8B55A247-FEBB-46DC-B681-4EFF804883AF - 2c4327bd6856b79e"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "modelTests.boardTests.test_isColumnFull() -&gt; ()"
moduleName = "modelTests"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/etudiant/Documents/puissance4/model/Tests/modelTests/BoardTests.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "65"
endingLineNumber = "65"
offsetFromSymbolStart = "1640">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

@ -36,6 +36,6 @@ if var board = b {
var winner : Player?
while winner == nil && !rule!.isGameOver(board: &board) {
winner = game?.tour()
winner = game.tour()
}
}

Loading…
Cancel
Save