Last active
July 3, 2019 06:57
-
-
Save epaga/720e8c8c31e53808dbf8b2243a9c8fea to your computer and use it in GitHub Desktop.
A simple SwiftUI Tic Tac Toe game I made together with my son to learn SwiftUI together
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// TicTacToe | |
// | |
// Created by John Goering on 08.06.19. | |
// Copyright © 2019 John Goering. All rights reserved. | |
// | |
import SwiftUI | |
import Combine | |
class GameData : BindableObject { | |
var turnIsX = true { | |
didSet { | |
didChange.send(Void()) | |
} | |
} | |
var game:[String] { | |
didSet { | |
didChange.send(Void()) | |
} | |
} | |
init(game:[String] = [ | |
" "," "," ", | |
" "," "," ", | |
" "," "," " | |
]) { | |
self.game = game | |
} | |
var didChange = PassthroughSubject<Void, Never>() | |
func reset() { | |
game = [ | |
" "," "," ", | |
" "," "," ", | |
" "," "," " | |
] | |
turnIsX = true | |
} | |
var winningIndexes: [Int]? { | |
get { | |
let waysToWin:[[Int]] = [ | |
[0,1,2], | |
[3,4,5], | |
[6,7,8], | |
[0,3,6], | |
[1,4,7], | |
[2,5,8], | |
[0,4,8], | |
[2,4,6] | |
] | |
return waysToWin.first{ | |
wayToWin in | |
return game[wayToWin[0]] == game[wayToWin[1]] && | |
game[wayToWin[1]] == game[wayToWin[2]] && | |
game[wayToWin[0]] != " " | |
} | |
} | |
} | |
var gameIsOver: Bool { | |
get { | |
return winningIndexes != nil || | |
game.first {$0 == " "} == nil | |
} | |
} | |
} | |
struct ContentView : View { | |
@ObjectBinding var game:GameData | |
var body: some View { | |
let turnMessage = game.gameIsOver ? "Game Over!" : | |
"It's \(game.turnIsX ? "X" : "O")'s turn!" | |
return ZStack { | |
VStack(spacing: 0) { | |
Text(turnMessage) | |
.font(.largeTitle) | |
.padding() | |
Spacer() | |
Row(rowIndex: 0, game:game) | |
Row(rowIndex: 1, game:game) | |
Row(rowIndex: 2, game:game) | |
Spacer() | |
}.background(Color(white: 0.8)) | |
if game.gameIsOver { | |
ResetButton(game: game) | |
} | |
} | |
} | |
} | |
struct ResetButton : View { | |
@ObjectBinding var game:GameData | |
var body: some View { | |
VStack { | |
Spacer() | |
Button(action: { | |
self.game.reset() | |
}) { | |
Text("Reset") | |
.font(.largeTitle) | |
} | |
.padding() | |
.background(Color.black) | |
.cornerRadius(10) | |
.offset(x: 0, y: -20) | |
} | |
} | |
} | |
struct Row : View { | |
var rowIndex:Int | |
@ObjectBinding var game:GameData | |
var body: some View { | |
HStack(spacing: 0) { | |
Field(rowIndex: rowIndex, colIndex: 0, game:game) | |
Field(rowIndex: rowIndex, colIndex: 1, game:game) | |
Field(rowIndex: rowIndex, colIndex: 2, game:game) | |
} | |
} | |
} | |
struct Field : View { | |
var rowIndex:Int | |
var colIndex:Int | |
@ObjectBinding var game:GameData | |
var body: some View { | |
let gameIndex = rowIndex * 3 + colIndex | |
let isWinningIndex = (game.winningIndexes ?? []).contains(gameIndex) | |
return ZStack { | |
if isWinningIndex { | |
Color.gray | |
.border(Color.black) | |
.animation(.basic()) | |
} else { | |
Color.white | |
.border(Color.black) | |
.animation(.basic()) | |
} | |
Text(game.game[gameIndex]) | |
.font(.system(size: 100)) | |
.color(isWinningIndex ? Color.red : Color.black ) | |
} | |
.tapAction { | |
if self.game.game[gameIndex] == " " { | |
if self.game.turnIsX { | |
self.game.game[gameIndex] = "X" | |
} else { | |
self.game.game[gameIndex] = "O" | |
} | |
self.game.turnIsX.toggle() | |
} | |
} | |
} | |
} | |
#if DEBUG | |
struct ContentView_Previews : PreviewProvider { | |
static var previews: some View { | |
ContentView(game:GameData(game: [ | |
"X"," "," ", | |
" ","O"," ", | |
" "," ","X" | |
])) | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
After closer inspection I have made some changes to the two platform initialisers and to the debug code initialisation:
or, 2. SceneDelegate.swift for iOS (this would have been in AppDelegate.swift in the past, but this split was split out for SwiftUI
And I noticed that debug game with its two Xs and one O was incorrectly initialised (given that the default for turnIsX = true); so I could have initialised the board to all " " (and at one stage I did), or rescued it as above from two Xs and an O to simple one X and one O; however I decided to set ip up follows, as it enables a discussion around forking as a strategy to win: