Skip to content

Commit 8bf02cc

Browse files
committed
flushed out the readme, added UIView subviews to display GenericGameGrid, and interact with it. Added Chess.swift.
1 parent 1ccb1d4 commit 8bf02cc

8 files changed

Lines changed: 533 additions & 45 deletions

File tree

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,48 @@ This is just a swift package containing a collection of classes I've found usefu
44

55
For now, use at your own risk.
66

7+
## Contents
78

9+
- `GenericGameGrid` - This is the meat and potatoes of this package, basically a way to hold a multidimensional array of state objects. (Note that, if you don't need complex state, `GridGame` assumes state is an `Int`. Warning that it might be removed in favor of `GenericGridGame<Int>`, which should be functionally identical.)
10+
- Most other structures in this package (`Coordinate` and `Direction` in particular) only exist to reference/access states in the `GenricGameGrid`.
11+
- `Chess` and `Tetromino` - These are simple structures meant to represent some relative grid movement.
12+
- `UIView+GenericGrid.swift` and `UIView+GenericGridInteraction.swift` – These files contain `UIView` subclasses specifically tailored to represent a `GenericGameGrid`.
13+
14+
## TODO
15+
16+
- [ ] some examples
17+
- [ ] getting started instructions
18+
- [ ] documentation
19+
- [ ] add a License
20+
- [ ] version 0.1
21+
22+
## Author
23+
24+
This is almost entirely the product of [Martin Grider](https://github.com/mgrider) futzing around.
25+
26+
## History
27+
28+
Once upon a time, many of these same concepts existed in an Objective-C package called [GenericGameModel](https://github.com/mgrider/GenericGameModel).
29+
30+
### fall 2024
31+
32+
- [x] added `UIView` subclasses
33+
(Feature parity with the original GGM? Not quite, I guess.)
34+
- [x] added `Chess.swift`
35+
36+
### summer 2024
37+
38+
- [x] added a generic version of `GridGame`
39+
- [x] added `Tetromino.swift`
40+
41+
### fall 2023
42+
43+
This project was created.
44+
45+
### fall 2021
46+
47+
A project called [EasyGameView](https://github.com/mgrider/EasyGameView) took some ideas from GGM and tried to apply them to SwiftUI. There was [an example project for EasyGameView](https://github.com/mgrider/EasyGameViewExample) for this package also.
48+
49+
### summer 2021
50+
51+
[GGMSwift](https://github.com/mgrider/GGMSwift) was created to (naively) port GGM to Swift.

Sources/SwiftGameUtils/Chess.swift

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import Foundation
2+
3+
/// A structure to handle Chess movement and relative positions.
4+
public struct Chess: Hashable, Equatable, Codable {
5+
6+
/// An enumeration of standard chess colors.
7+
public enum Color: String, Hashable, Equatable, Codable {
8+
case black
9+
case white
10+
}
11+
12+
/// An enumeration of standard piece types, plus special types `.empty`, and `.invalid`.
13+
public enum Piece: String, Hashable, Equatable, Codable {
14+
case bishop
15+
case pawn
16+
case queen
17+
case king
18+
case knight
19+
case rook
20+
21+
/// This represents an empty space on a chessboard.
22+
case empty
23+
24+
/// This represents an invalid `Piece` type, in case that is needed for some reason.
25+
case invalid
26+
27+
/// An array of all the valid piece types.
28+
public static var allValidTypes: [Piece] {
29+
return [
30+
.bishop,
31+
.pawn,
32+
.queen,
33+
.king,
34+
.knight,
35+
.rook,
36+
]
37+
}
38+
39+
/// An array of possible capture directions as `Coordinate` offsets.
40+
///
41+
/// Notes:
42+
/// - For `.pawn`, positive `y` movement is assumed. (Meaning this doesn't consider color.)
43+
/// - For pieces that can capture more than one space away in a given direction, a single `Coordinate`
44+
/// value for each direction is returned. See also `possibleMovementRecurses(forPiece:)`.
45+
/// - See also `possibleMovementCoordinates(forPiece:)`.
46+
public static func possibleCaptureCoordinates(
47+
forPiece piece: Piece
48+
) -> [Coordinate] {
49+
switch piece {
50+
case .bishop:
51+
return Direction.diagonalOffsets
52+
case .pawn:
53+
return [
54+
Coordinate(inDirection: .upLeft),
55+
Coordinate(inDirection: .upRight),
56+
]
57+
case .queen:
58+
return Direction.allOffsets
59+
case .king:
60+
return Direction.allOffsets
61+
case .knight:
62+
return [
63+
Coordinate(x: -1, y: 2),
64+
Coordinate(x: -2, y: 1),
65+
Coordinate(x: -2, y: -1),
66+
Coordinate(x: -1, y: -2),
67+
Coordinate(x: 1, y: 2),
68+
Coordinate(x: 2, y: 1),
69+
Coordinate(x: 2, y: -1),
70+
Coordinate(x: 1, y: -2),
71+
]
72+
case .rook:
73+
return Direction.orthogonalOffsets
74+
case .empty, .invalid:
75+
return []
76+
}
77+
}
78+
79+
/// An array of possible movement directions as `Coordinate` offsets.
80+
///
81+
/// Notes:
82+
/// - For `.pawn`, positive y movement is assumed. (Meaning this doesn't consider color.)
83+
/// - For pieces that can move more than one space in a given direction, a single `Coordinate`
84+
/// value for each direction is returned. See also `possibleMovementRecurses(forPiece:)`.
85+
/// - See also `possibleCaptureCoordinates(forPiece:)`.
86+
public static func possibleMovementCoordinates(
87+
forPiece piece: Piece
88+
) -> [Coordinate] {
89+
switch piece {
90+
case .bishop:
91+
return Direction.diagonalOffsets
92+
case .pawn:
93+
return [
94+
Coordinate(x: 0, y: 1),
95+
]
96+
case .queen:
97+
return Direction.allOffsets
98+
case .king:
99+
return Direction.allOffsets
100+
case .knight:
101+
return [
102+
Coordinate(x: -1, y: 2),
103+
Coordinate(x: -2, y: 1),
104+
Coordinate(x: -2, y: -1),
105+
Coordinate(x: -1, y: -2),
106+
Coordinate(x: 1, y: 2),
107+
Coordinate(x: 2, y: 1),
108+
Coordinate(x: 2, y: -1),
109+
Coordinate(x: 1, y: -2),
110+
]
111+
case .rook:
112+
return Direction.orthogonalOffsets
113+
case .empty, .invalid:
114+
return []
115+
}
116+
}
117+
118+
/// A boolean value indicating whether a given `Piece` type can move (or capture) more than
119+
/// one space in the `Coordinate` direction indicated by `possibleMovementCoordinates` or
120+
/// `possibleCaptureCoordinates` respectively.
121+
public static func possibleMovementRecurses(
122+
forPiece piece: Piece
123+
) -> Bool {
124+
switch piece {
125+
case .bishop, .queen, .rook:
126+
return true
127+
case .pawn, .king, .knight:
128+
return false
129+
case .empty, .invalid:
130+
return false
131+
}
132+
}
133+
134+
/// A randomly selected valid piece type.
135+
public static func random() -> Piece {
136+
let range = 0..<allValidTypes.count
137+
let index = Int.random(in: range)
138+
return Chess.Piece.allValidTypes[index]
139+
}
140+
141+
/// An array of the whole set, in the proper distributions.
142+
public static var theWholeSet: [Piece] {
143+
return [
144+
.king, .queen, .rook, .rook, .bishop, .bishop, .knight, .knight,
145+
.pawn, .pawn, .pawn, .pawn, .pawn, .pawn, .pawn, .pawn,
146+
]
147+
}
148+
}
149+
}

Sources/SwiftGameUtils/Coordinate.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ public struct Coordinate: CoordinateProtocol {
1616
self.y = y
1717
}
1818

19+
public init(inDirection direction: Direction) {
20+
self = direction.offset()
21+
}
22+
1923
/// Implementing Hashable protocol
2024
public func hash(into hasher: inout Hasher) {
2125
hasher.combine(x)
Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,62 @@
11
import Foundation
22

33
/// For relating positions to one another.
4+
///
5+
/// Note that all coordinate offsets assume positive `y` is `.up`, and positive `x` is `.right`
46
public enum Direction: CaseIterable {
5-
case up
67
case down
8+
case downLeft
9+
case downRight
710
case left
811
case right
12+
case up
13+
case upLeft
14+
case upRight
915

10-
public func coordinateOffset() -> Coordinate {
11-
switch self {
12-
case .up:
13-
return Coordinate(0,1)
16+
/// Returns a `Coordinate` offset in the given `Direction`.
17+
public func offset() -> Coordinate {
18+
return Direction.offset(from: self)
19+
}
20+
21+
// MARK: static functions
22+
23+
/// An array of all direction offsets.
24+
public static let allOffsets: [Coordinate] = Direction.allCases.map { $0.offset() }
25+
26+
/// An array of all diagonal directions.
27+
public static let diagonal: [Direction] = [.upLeft, .upRight, .downLeft, .downRight]
28+
29+
/// An array of all diagonal direction offsets.
30+
public static let diagonalOffsets: [Coordinate] = Direction.diagonal.map { $0.offset() }
31+
32+
/// A static function that returns a `Coordinate` offset in the given `Direction`.
33+
public static func offset(
34+
from direction: Direction
35+
) -> Coordinate {
36+
switch direction {
1437
case .down:
15-
return Coordinate(0,-1)
38+
return Coordinate(x: 0, y: -1)
39+
case .downLeft:
40+
return Coordinate(x: -1, y: -1)
41+
case .downRight:
42+
return Coordinate(x: 1, y: -1)
1643
case .left:
17-
return Coordinate(-1,0)
44+
return Coordinate(x: -1, y: 0)
1845
case .right:
19-
return Coordinate(1,0)
46+
return Coordinate(x: 1, y: 0)
47+
case .up:
48+
return Coordinate(x: 0, y: 1)
49+
case .upLeft:
50+
return Coordinate(x: -1, y: 1)
51+
case .upRight:
52+
return Coordinate(x: 1, y: 1)
2053
}
2154
}
55+
56+
/// An array of all orthogonal directions.
57+
public static let orthogonal: [Direction] = [.up, .down, .left, .right]
58+
59+
/// An array of all orthogonal direction offsets.
60+
public static let orthogonalOffsets: [Coordinate] = Direction.orthogonal.map { $0.offset() }
61+
2262
}

Sources/SwiftGameUtils/GenericGridGame.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,16 +186,24 @@ public struct GenericGridGame<StateType: GenericGridGameStateProtocol>: Codable,
186186

187187
// MARK: randomization
188188

189-
/// get a random state from the initially provided array
190-
public func randomStateInt() -> StateType {
189+
/// get a random state from the array stored in `statesPossibleRandom`.
190+
///
191+
/// TODO: make this use a predictable randomizer (maybe one that conforms to `RandomNumberGenerator`)
192+
///
193+
public func randomState() -> StateType {
191194
return statesPossibleRandom[Int.random(in: 0..<statesPossibleRandom.count)]
192195
}
193196

194-
/// completely randomize the grid states with random values
197+
/// randomize a single state from the states stored in `statesPossibleRandom`.
198+
mutating public func randomizeState(at coordinate: Coordinate) {
199+
states[coordinate] = randomState()
200+
}
201+
202+
/// completely randomize the grid states with random values stored in `statesPossibleRandom`.
195203
mutating public func randomizeStates() {
196204
for y in 0..<gridHeight {
197205
for x in 0..<gridWidth {
198-
states[Coordinate(x: x, y: y)] = randomStateInt()
206+
states[Coordinate(x: x, y: y)] = randomState()
199207
}
200208
}
201209
}

Sources/SwiftGameUtils/Tetromino.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Foundation
22

3-
/// A class to handle Tetromino rotations and positions
3+
/// A structure to handle Tetromino rotations and positions
44
public struct Tetromino: Hashable, Equatable, Codable {
55

66
public enum Shape: Int, Hashable, Equatable, Codable {

0 commit comments

Comments
 (0)