Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions Chess.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

/* Begin PBXBuildFile section */
0558E89E2F0BBAE8006EE0FA /* rook.icon in Resources */ = {isa = PBXBuildFile; fileRef = 0558E89D2F0BBAE8006EE0FA /* rook.icon */; };
05F753BD2F2159080017ADA8 /* SaveGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05F753BC2F2159050017ADA8 /* SaveGame.swift */; };
05F753BF2F2159170017ADA8 /* SaveCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05F753BE2F2159160017ADA8 /* SaveCoordinator.swift */; };
05F753C92F217B0F0017ADA8 /* GameSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05F753C72F217B0E0017ADA8 /* GameSnapshot.swift */; };
561B67F328D2604600D6E2F7 /* BoardInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 561B67F228D2604600D6E2F7 /* BoardInteractor.swift */; };
561B67F828D4C12F00D6E2F7 /* PickExpectation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 561B67F728D4C12F00D6E2F7 /* PickExpectation.swift */; };
561B67FC28D4C14700D6E2F7 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = 561B67FB28D4C14700D6E2F7 /* Action.swift */; };
Expand Down Expand Up @@ -105,6 +108,9 @@

/* Begin PBXFileReference section */
0558E89D2F0BBAE8006EE0FA /* rook.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = rook.icon; sourceTree = "<group>"; };
05F753BC2F2159050017ADA8 /* SaveGame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveGame.swift; sourceTree = "<group>"; };
05F753BE2F2159160017ADA8 /* SaveCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveCoordinator.swift; sourceTree = "<group>"; };
05F753C72F217B0E0017ADA8 /* GameSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSnapshot.swift; sourceTree = "<group>"; };
561B67F228D2604600D6E2F7 /* BoardInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardInteractor.swift; sourceTree = "<group>"; };
561B67F728D4C12F00D6E2F7 /* PickExpectation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickExpectation.swift; sourceTree = "<group>"; };
561B67FB28D4C14700D6E2F7 /* Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -183,6 +189,15 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
05F753BB2F2158FF0017ADA8 /* Save */ = {
isa = PBXGroup;
children = (
05F753BE2F2159160017ADA8 /* SaveCoordinator.swift */,
05F753BC2F2159050017ADA8 /* SaveGame.swift */,
);
path = Save;
sourceTree = "<group>";
};
56122C1728DB36A800D31958 /* Storyboards */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -349,6 +364,8 @@
569699C228CE479700469E6B /* Game */ = {
isa = PBXGroup;
children = (
05F753C72F217B0E0017ADA8 /* GameSnapshot.swift */,
05F753BB2F2158FF0017ADA8 /* Save */,
562D0E6E28E1E60300944DF2 /* Field */,
561B67FA28D4C13C00D6E2F7 /* Move */,
561B67F528D2604900D6E2F7 /* Board */,
Expand Down Expand Up @@ -600,6 +617,7 @@
5648531728C90D7F00BA4CC0 /* Move.swift in Sources */,
561B67FC28D4C14700D6E2F7 /* Action.swift in Sources */,
5648531128C90B5C00BA4CC0 /* PieceColor.swift in Sources */,
05F753BF2F2159170017ADA8 /* SaveCoordinator.swift in Sources */,
56BA8DEA28DF09F80012A5CE /* QueenMoveGenerator.swift in Sources */,
56BB94AE28A94A750034D4AB /* GameViewController.swift in Sources */,
561B67F328D2604600D6E2F7 /* BoardInteractor.swift in Sources */,
Expand All @@ -613,6 +631,7 @@
56BA8DDC28DE59C40012A5CE /* HistoryBrowser.swift in Sources */,
567A568528CB68BF008DDE40 /* CameraController.swift in Sources */,
5648531528C90B9100BA4CC0 /* Piece.swift in Sources */,
05F753C92F217B0F0017ADA8 /* GameSnapshot.swift in Sources */,
5648530F28C90B2600BA4CC0 /* PieceType.swift in Sources */,
56D8FCED28E4EE0400027132 /* PieceParser.swift in Sources */,
562D0E7128E1E60E00944DF2 /* FieldParser.swift in Sources */,
Expand All @@ -623,6 +642,7 @@
56BA8DE428DF08110012A5CE /* RookMoveGenerator.swift in Sources */,
569699CA28CE4DD300469E6B /* Field.swift in Sources */,
56BA8DE828DF09BF0012A5CE /* KingMoveGenerator.swift in Sources */,
05F753BD2F2159080017ADA8 /* SaveGame.swift in Sources */,
567A568828CBA420008DDE40 /* MouseInteractionHandler.swift in Sources */,
561B67F828D4C12F00D6E2F7 /* PickExpectation.swift in Sources */,
56C3174728CFFDCB00C04F3C /* MovesGenerator.swift in Sources */,
Expand Down Expand Up @@ -720,7 +740,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MACOSX_DEPLOYMENT_TARGET = 14.6;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
Expand Down Expand Up @@ -776,7 +796,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MACOSX_DEPLOYMENT_TARGET = 14.6;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
Expand Down Expand Up @@ -847,7 +867,6 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.5;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.stompor.mateusz.porcelain.chess.tests;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -864,7 +883,6 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.5;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.stompor.mateusz.porcelain.chess.tests;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
5 changes: 1 addition & 4 deletions Chess/Common/Game/CheckState.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//
// CheckState.swift
// Chess
//
// Created by Mateusz Stompór on 26/09/2022.
// Copyright © 2022 Mateusz Stompór. All rights reserved.
//

enum CheckState: Codable {
Expand Down
22 changes: 22 additions & 0 deletions Chess/Common/Game/GameSnapshot.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Copyright © 2026 Mateusz Stompór. All rights reserved.
//

class GameSnapshot: Codable {
var board: Board
var selectedPiece: Piece?
var turn: PieceColor
var expectation: PickExpectation
var checkState: CheckState
init(board: Board,
selectedPiece: Piece? = nil,
turn: PieceColor,
expectation: PickExpectation,
checkState: CheckState) {
self.board = board
self.selectedPiece = selectedPiece
self.turn = turn
self.expectation = expectation
self.checkState = checkState
}
}
58 changes: 42 additions & 16 deletions Chess/Common/Game/GameState.swift
Original file line number Diff line number Diff line change
@@ -1,29 +1,55 @@
//
// GameState.swift
// Chess
//
// Created by Mateusz Stompór on 16/09/2022.
// Copyright © 2022 Mateusz Stompór. All rights reserved.
//

import SwiftData

@Model
class GameState {
let previous: GameState?
let board: Board
let selectedPiece: Piece?
let turn: PieceColor
let expectation: PickExpectation
let checkState: CheckState
var all: [GameSnapshot]
var last: GameSnapshot {
guard let lastState = all.last else {
fatalError("Invalid state, game must have at least one board")
}
return lastState
}
var board: Board {
last.board
}
var selectedPiece: Piece? {
last.selectedPiece
}
var turn: PieceColor {
last.turn
}
var expectation: PickExpectation {
last.expectation
}
var checkState: CheckState {
last.checkState
}
var previous: GameState? {
guard all.count > 1 else {
return nil
}
return GameState(all: Array(all.dropLast()))
}
private init(all: [GameSnapshot]) {
self.all = all
}
init(previous: GameState?,
board: Board,
selectedPiece: Piece?,
turn: PieceColor,
expectation: PickExpectation,
checkState: CheckState) {
self.previous = previous
self.board = board
self.selectedPiece = selectedPiece
self.turn = turn
self.expectation = expectation
self.checkState = checkState
let allPrevious = previous?.all ?? []
let current = GameSnapshot(board: board,
selectedPiece: selectedPiece,
turn: turn,
expectation: expectation,
checkState: checkState)
all = allPrevious + [current]
}
static var initial: GameState {
GameState(previous: nil,
Expand Down
5 changes: 1 addition & 4 deletions Chess/Common/Game/HistoryBrowser.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//
// HistoryBrowser.swift
// Chess
//
// Created by Mateusz Stompór on 23/09/2022.
// Copyright © 2022 Mateusz Stompór. All rights reserved.
//

class HistoryBrowser {
Expand Down
5 changes: 1 addition & 4 deletions Chess/Common/Game/PickExpectation.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//
// PickExpectation.swift
// Chess
//
// Created by Mateusz Stompór on 16/09/2022.
// Copyright © 2022 Mateusz Stompór. All rights reserved.
//

enum PickExpectation: Codable {
Expand Down
83 changes: 83 additions & 0 deletions Chess/Common/Game/Save/SaveCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//
// Copyright © 2022 Mateusz Stompór. All rights reserved.
//

import Combine
import SwiftData

class SaveCoordinator {
let saveDidChange = PassthroughSubject<Void, Never>()
private let container: ModelContainer
static let shared: SaveCoordinator = {
SaveCoordinator()
}()
private init() {
do {
container = try ModelContainer(for: SaveGame.self)
} catch {
fatalError("Failed to create ModelContainer: \(error)")
}
}
func save(game: SaveGame) -> Bool {
let context = ModelContext(container)
save(state: game.state, context: context)
context.insert(game)
do {
try context.save()
} catch {
print("Failed to insert: \(error)")
return false
}
saveDidChange.send()
return true
}
private func save(state: GameState, context: ModelContext) {
context.insert(state)
if let previous = state.previous {
save(state: previous, context: context)
}
}
var saveCount: Int {
all.count
}
var all: [SaveGame] {
let context = ModelContext(container)

let fetchDescriptor = FetchDescriptor<SaveGame>()
do {
return try context.fetch(fetchDescriptor)
} catch {
return []
}
}
func load(index: Int) -> SaveGame? {
let context = ModelContext(container)

let fetchDescriptor = FetchDescriptor<SaveGame>()
do {
return try context.fetch(fetchDescriptor)[index]
} catch {
return nil
}
}
func wipe() -> Bool {
let context = ModelContext(container)

let fetchDescriptor = FetchDescriptor<SaveGame>()
do {
let saves = try context.fetch(fetchDescriptor)

for save in saves {
context.delete(save)
}

try context.save()
print("All save records deleted!")
} catch {
print("Failed to delete: \(error)")
return false
}
saveDidChange.send()
return true
}
}
16 changes: 16 additions & 0 deletions Chess/Common/Game/Save/SaveGame.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Copyright © 2022 Mateusz Stompór. All rights reserved.
//

import Foundation
import SwiftData

@Model
class SaveGame {
var creationDate: Date
var state: GameState
init(creationDate: Date, state: GameState) {
self.creationDate = creationDate
self.state = state
}
}
5 changes: 1 addition & 4 deletions Chess/Common/Scene/ResourceLoader.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//
// ResourceLoader.swift
// Chess
//
// Created by Mateusz Stompór on 06/09/2022.
// Copyright © 2022 Mateusz Stompór. All rights reserved.
//

import MetalKit
Expand Down
5 changes: 1 addition & 4 deletions Chess/Common/Scene/SceneBuilder.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//
// SceneBuilder.swift
// Chess
//
// Created by Mateusz Stompór on 06/09/2022.
// Copyright © 2022 Mateusz Stompór. All rights reserved.
//

import Metal
Expand Down
Loading