Skip to content

Commit f8aa57e

Browse files
committed
Added tictactoe
1 parent bfd0a69 commit f8aa57e

File tree

1 file changed

+139
-5
lines changed
  • Classic Computer Science Problems in Swift.playground/Pages/Chapter 8.xcplaygroundpage

1 file changed

+139
-5
lines changed

Classic Computer Science Problems in Swift.playground/Pages/Chapter 8.xcplaygroundpage/Contents.swift

Lines changed: 139 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,146 @@ func solveTSP<T>(cities: [T], distances: [T: [T: Int]]) -> (solution: [T], dista
142142
let vtTSP = solveTSP(cities: vtCities, distances: vtDistances)
143143
print("The shortest path is \(vtTSP.solution) in \(vtTSP.distance) miles.")
144144

145-
//func dog(test: inout [String]) {
146-
// test.append("hello")
147-
//
148-
//}
145+
/// Phone Number Mnemonics
146+
147+
let phoneMapping: [Character: [Character]] = ["1": ["1"], "2": ["a", "b", "c"], "3": ["d", "e", "f"], "4": ["g", "h", "i"], "5": ["j", "k", "l"], "6": ["m", "n", "o"], "7": ["p", "q", "r", "s"], "8": ["t", "u", "v"], "9": ["w", "x", "y", "z"], "0": ["0"]]
148+
149+
150+
// return all of the possible characters combos, given a mapping, for a given number
151+
func stringToPossibilities(_ s: String, mapping: [Character: [Character]]) -> [[Character]]{
152+
let possibilities = s.characters.flatMap{ mapping[$0] }
153+
print(possibilities)
154+
return combineAllPossibilities(possibilities)
155+
}
156+
157+
// takes a set of possible characters for each position and finds all possible permutations
158+
func combineAllPossibilities(_ possibilities: [[Character]]) -> [[Character]] {
159+
guard let possibility = possibilities.first else { return [[]] }
160+
var permutations: [[Character]] = possibility.map { [$0] } // turn each into an array
161+
for possibility in possibilities[1..<possibilities.count] where possibility != [] {
162+
let toRemove = permutations.count // temp
163+
for permutation in permutations {
164+
for c in possibility { // try adding every letter
165+
var newPermutation: [Character] = permutation // need a mutable copy
166+
newPermutation.append(c) // add character on the end
167+
permutations.append(newPermutation) // new combo ready
168+
}
169+
}
170+
permutations.removeFirst(toRemove) // remove combos missing new last letter
171+
}
172+
return permutations
173+
}
174+
175+
let permutations = stringToPossibilities("1440787", mapping: phoneMapping)
176+
177+
/// Tic-tac-toe
178+
179+
enum Piece: String {
180+
case X = "X"
181+
case O = "O"
182+
case E = " "
183+
var opposite: Piece {
184+
switch self {
185+
case .X:
186+
return .O
187+
case .O:
188+
return .X
189+
case .E:
190+
return .E
191+
}
192+
}
193+
}
194+
195+
// a move is an integer 0-9 indicating a place to put a piece
196+
typealias Move = Int
197+
198+
struct Board {
199+
let position: [Piece]
200+
let turn: Piece
201+
let lastMove: Move
202+
203+
// the legal moves in a position are all of the empty squares
204+
var legalMoves: [Move] {
205+
return position.indices.filter { position[$0] == .E }
206+
}
207+
208+
// by default the board is empty and X goes first
209+
// lastMove being -1 is a marker of a start position
210+
init(position: [Piece] = [.E, .E, .E, .E, .E, .E, .E, .E, .E], turn: Piece = .X, lastMove: Int = -1) {
211+
self.position = position
212+
self.turn = turn
213+
self.lastMove = lastMove
214+
}
215+
216+
// location can be 0-8, indicating where to move
217+
// return a new board with the move played
218+
func move(_ location: Move) -> Board {
219+
var tempPosition = position
220+
tempPosition[location] = turn
221+
return Board(position: tempPosition, turn: turn.opposite, lastMove: location)
222+
}
223+
224+
var isWin: Bool {
225+
return
226+
position[0] == position[1] && position [0] == position[2] && position[0] != .E || // row 0
227+
position[3] == position[4] && position[3] == position [5] && position[3] != .E || // row 1
228+
position[6] == position[7] && position[6] == position[8] && position[6] != .E || // row 2
229+
position[0] == position[3] && position[0] == position[6] && position[0] != .E || // col 0
230+
position[1] == position[4] && position[1] == position[7] && position[1] != .E || // col 1
231+
position[2] == position[5] && position[2] == position[8] && position[2] != .E || // col 2
232+
position[0] == position[4] && position[0] == position[8] && position[0] != .E || // diag 0
233+
position[2] == position[4] && position[2] == position[6] && position[2] != .E // diag 1
234+
235+
}
236+
237+
var isDraw: Bool {
238+
return !isWin && legalMoves.count == 0
239+
}
240+
}
241+
242+
func minimax(_ board: Board, maximizing: Bool) -> (eval: Int, bestMove: Move) {
243+
if board.isWin { return (1, board.lastMove) }
244+
else if board.isDraw { return (0, board.lastMove) }
245+
246+
if maximizing {
247+
var bestEval: (eval: Int, bestMove: Move) = (Int.min, -1)
248+
for move in board.legalMoves {
249+
let result = minimax(board.move(move), maximizing: false)
250+
if result.eval > bestEval.eval { bestEval = result }
251+
}
252+
return bestEval
253+
} else { // minimizing
254+
var worstEval: (eval: Int, bestMove: Move) = (Int.max, -1)
255+
for move in board.legalMoves {
256+
let result = minimax(board.move(move), maximizing: true)
257+
if result.eval < worstEval.eval { worstEval = result }
258+
}
259+
return worstEval
260+
}
261+
}
262+
263+
// win in 1 move
264+
let toWinEasyPosition: [Piece] = [.X, .O, .X,
265+
.X, .E, .O,
266+
.E, .E, .O]
267+
let testBoard1: Board = Board(position: toWinEasyPosition, turn: .X, lastMove: 8)
268+
let answer1 = minimax(testBoard1, maximizing: true)
269+
print(answer1.bestMove)
149270

150-
// City 0
271+
// must block O's win
272+
let toBlockPosition: [Piece] = [.X, .E, .E,
273+
.E, .E, .O,
274+
.E, .X, .O]
275+
let testBoard2: Board = Board(position: toBlockPosition, turn: .X, lastMove: 8)
276+
let answer2 = minimax(testBoard2, maximizing: true)
277+
print(answer2.bestMove)
151278

279+
// find the best move to win in 2 moves
280+
let toWinHardPosition: [Piece] = [.X, .E, .E,
281+
.E, .E, .O,
282+
.O, .X, .E]
283+
let testBoard3: Board = Board(position: toWinHardPosition, turn: .X, lastMove: 6)
284+
let answer3 = minimax(testBoard3, maximizing: true)
285+
print(answer3.bestMove)
152286
//: [Next](@next)
153287

0 commit comments

Comments
 (0)