Last active
March 23, 2018 16:37
-
-
Save nerdo/24d24a432bcde285f42e69328bbcbca4 to your computer and use it in GitHub Desktop.
ReSwift Tic Tac Toe
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
<?xml version="1.0" encoding="UTF-8"?> | |
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> | |
<device id="retina4_7" orientation="portrait"> | |
<adaptation id="fullscreen"/> | |
</device> | |
<dependencies> | |
<deployment identifier="iOS"/> | |
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/> | |
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/> | |
<capability name="Safe area layout guides" minToolsVersion="9.0"/> | |
<capability name="Stack View standard spacing" minToolsVersion="9.0"/> | |
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | |
</dependencies> | |
<scenes> | |
<!--View Controller--> | |
<scene sceneID="tne-QT-ifu"> | |
<objects> | |
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="TicTacToe" customModuleProvider="target" sceneMemberID="viewController"> | |
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> | |
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | |
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | |
<subviews> | |
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="GYC-JQ-41v"> | |
<rect key="frame" x="15" y="161" width="345" height="345"/> | |
<subviews> | |
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="v7b-c4-5Ke"> | |
<rect key="frame" x="0.0" y="0.0" width="345" height="109.5"/> | |
<subviews> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2cU-au-Mow"> | |
<rect key="frame" x="0.0" y="0.0" width="109.5" height="109.5"/> | |
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="32"/> | |
<state key="normal"> | |
<color key="titleColor" red="0.12984204290000001" green="0.12984612579999999" blue="0.12984395030000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
</state> | |
<connections> | |
<action selector="button0Pressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="82X-ZM-363"/> | |
</connections> | |
</button> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="drs-Bo-49n"> | |
<rect key="frame" x="117.5" y="0.0" width="110" height="109.5"/> | |
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="32"/> | |
<state key="normal"> | |
<color key="titleColor" red="0.12984204290000001" green="0.12984612579999999" blue="0.12984395030000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
</state> | |
<connections> | |
<action selector="button1Pressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="y6O-wU-B7p"/> | |
</connections> | |
</button> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="C4m-5v-2rO"> | |
<rect key="frame" x="235.5" y="0.0" width="109.5" height="109.5"/> | |
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="32"/> | |
<state key="normal"> | |
<color key="titleColor" red="0.12984204290000001" green="0.12984612579999999" blue="0.12984395030000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
</state> | |
<connections> | |
<action selector="button2Pressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="KVg-VM-FO8"/> | |
</connections> | |
</button> | |
</subviews> | |
</stackView> | |
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="IDm-oR-oQE"> | |
<rect key="frame" x="0.0" y="117.5" width="345" height="110"/> | |
<subviews> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="YtA-u5-gCw"> | |
<rect key="frame" x="0.0" y="0.0" width="109.5" height="110"/> | |
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="32"/> | |
<state key="normal"> | |
<color key="titleColor" red="0.12984204290000001" green="0.12984612579999999" blue="0.12984395030000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
</state> | |
<connections> | |
<action selector="button3Pressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="M7o-Ua-KhF"/> | |
</connections> | |
</button> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lnr-Ld-pnz"> | |
<rect key="frame" x="117.5" y="0.0" width="110" height="110"/> | |
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="32"/> | |
<state key="normal"> | |
<color key="titleColor" red="0.12984204290000001" green="0.12984612579999999" blue="0.12984395030000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
</state> | |
<connections> | |
<action selector="button4Pressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="wNY-9B-ocj"/> | |
</connections> | |
</button> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tTU-Xd-RTN"> | |
<rect key="frame" x="235.5" y="0.0" width="109.5" height="110"/> | |
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="32"/> | |
<state key="normal"> | |
<color key="titleColor" red="0.12984204290000001" green="0.12984612579999999" blue="0.12984395030000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
</state> | |
<connections> | |
<action selector="button5Pressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="yNV-Mf-v4H"/> | |
</connections> | |
</button> | |
</subviews> | |
</stackView> | |
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="wO4-X9-a35"> | |
<rect key="frame" x="0.0" y="235.5" width="345" height="109.5"/> | |
<subviews> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="S5y-Gp-PXp"> | |
<rect key="frame" x="0.0" y="0.0" width="109.5" height="109.5"/> | |
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="32"/> | |
<state key="normal"> | |
<color key="titleColor" red="0.12984204290000001" green="0.12984612579999999" blue="0.12984395030000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
</state> | |
<connections> | |
<action selector="button6Pressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="fwN-Ea-0nP"/> | |
</connections> | |
</button> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DZE-sb-KSR"> | |
<rect key="frame" x="117.5" y="0.0" width="110" height="109.5"/> | |
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="32"/> | |
<state key="normal"> | |
<color key="titleColor" red="0.12984204290000001" green="0.12984612579999999" blue="0.12984395030000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
</state> | |
<connections> | |
<action selector="button7Pressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="qKa-ck-me6"/> | |
</connections> | |
</button> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HN8-ZY-CpP"> | |
<rect key="frame" x="235.5" y="0.0" width="109.5" height="109.5"/> | |
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="32"/> | |
<state key="normal"> | |
<color key="titleColor" red="0.12984204290000001" green="0.12984612579999999" blue="0.12984395030000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
</state> | |
<connections> | |
<action selector="button8Pressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="Ule-JU-klE"/> | |
</connections> | |
</button> | |
</subviews> | |
</stackView> | |
</subviews> | |
<constraints> | |
<constraint firstAttribute="width" secondItem="GYC-JQ-41v" secondAttribute="height" multiplier="1:1" id="1Ir-Zn-ZiX"/> | |
</constraints> | |
</stackView> | |
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="YVy-GY-iZt"> | |
<rect key="frame" x="111.5" y="104" width="152" height="49"/> | |
<color key="backgroundColor" red="0.016804177310000001" green="0.19835099580000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<fontDescription key="fontDescription" type="system" pointSize="24"/> | |
<inset key="contentEdgeInsets" minX="10" minY="10" maxX="10" maxY="10"/> | |
<state key="normal" title="PLAY AGAIN"> | |
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | |
</state> | |
<connections> | |
<action selector="playAgain:" destination="BYZ-38-t0r" eventType="touchUpInside" id="y0M-27-msF"/> | |
</connections> | |
</button> | |
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Z Won!" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XL9-cY-Sce"> | |
<rect key="frame" x="138" y="514" width="99" height="38.5"/> | |
<fontDescription key="fontDescription" type="system" pointSize="32"/> | |
<nil key="textColor"/> | |
<nil key="highlightedColor"/> | |
</label> | |
</subviews> | |
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | |
<constraints> | |
<constraint firstItem="XL9-cY-Sce" firstAttribute="centerX" secondItem="GYC-JQ-41v" secondAttribute="centerX" id="0S8-2B-MAc"/> | |
<constraint firstItem="GYC-JQ-41v" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="5my-Tr-Ff6"/> | |
<constraint firstItem="GYC-JQ-41v" firstAttribute="top" relation="greaterThanOrEqual" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="65" id="D0B-8f-seN"/> | |
<constraint firstItem="XL9-cY-Sce" firstAttribute="top" secondItem="GYC-JQ-41v" secondAttribute="bottom" constant="8" symbolic="YES" id="Ecy-HS-Eaw"/> | |
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="GYC-JQ-41v" secondAttribute="trailing" constant="15" id="SAa-sC-3vU"/> | |
<constraint firstItem="GYC-JQ-41v" firstAttribute="height" secondItem="6Tk-OE-BBY" secondAttribute="height" priority="750" id="SGK-vz-AuA"/> | |
<constraint firstItem="YVy-GY-iZt" firstAttribute="centerX" secondItem="GYC-JQ-41v" secondAttribute="centerX" id="XeQ-MS-qYm"/> | |
<constraint firstItem="GYC-JQ-41v" firstAttribute="top" secondItem="YVy-GY-iZt" secondAttribute="bottom" constant="8" symbolic="YES" id="cua-PS-2Ah"/> | |
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="GYC-JQ-41v" secondAttribute="bottom" constant="50" id="pDf-n6-QyB"/> | |
<constraint firstItem="GYC-JQ-41v" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="15" id="rP6-im-sYS"/> | |
<constraint firstItem="GYC-JQ-41v" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="y7y-qw-7Jy"/> | |
</constraints> | |
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> | |
<variation key="default"> | |
<mask key="constraints"> | |
<exclude reference="pDf-n6-QyB"/> | |
<exclude reference="D0B-8f-seN"/> | |
<exclude reference="SGK-vz-AuA"/> | |
</mask> | |
</variation> | |
<variation key="heightClass=compact"> | |
<mask key="constraints"> | |
<exclude reference="SAa-sC-3vU"/> | |
<include reference="pDf-n6-QyB"/> | |
<include reference="D0B-8f-seN"/> | |
<include reference="SGK-vz-AuA"/> | |
<exclude reference="rP6-im-sYS"/> | |
</mask> | |
</variation> | |
</view> | |
<connections> | |
<outlet property="button0" destination="2cU-au-Mow" id="udq-uF-Eak"/> | |
<outlet property="button1" destination="drs-Bo-49n" id="PrU-gF-4oa"/> | |
<outlet property="button2" destination="C4m-5v-2rO" id="fdE-sp-oC5"/> | |
<outlet property="button3" destination="YtA-u5-gCw" id="3Vz-n5-w01"/> | |
<outlet property="button4" destination="lnr-Ld-pnz" id="rZc-UI-8Sc"/> | |
<outlet property="button5" destination="tTU-Xd-RTN" id="cIr-hZ-04Q"/> | |
<outlet property="button6" destination="S5y-Gp-PXp" id="yMU-9m-uCS"/> | |
<outlet property="button7" destination="DZE-sb-KSR" id="HcP-JR-cxR"/> | |
<outlet property="button8" destination="HN8-ZY-CpP" id="410-BS-gY0"/> | |
<outlet property="playAgainButton" destination="YVy-GY-iZt" id="czv-PK-bW4"/> | |
<outlet property="winnerLabel" destination="XL9-cY-Sce" id="dG6-ga-Uyr"/> | |
</connections> | |
</viewController> | |
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> | |
</objects> | |
</scene> | |
</scenes> | |
</document> |
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
// | |
// TicTacToe.swift | |
// TicTacToe | |
// | |
// Created by Dannel Albert on 3/22/18. | |
// Copyright © 2018 Dannel Albert. All rights reserved. | |
// | |
import ReSwift | |
open class TicTacToe { | |
public typealias Winner = (player: Player, path: [Int]) | |
public static var store = Store<State>(reducer: Reducer.ticTacToe, state: nil) | |
public enum Player: String { | |
case x = "X" | |
case o = "O" | |
} | |
public struct State: ReSwift.StateType { | |
var board: [Player?] = [ | |
nil, nil, nil, | |
nil, nil, nil, | |
nil, nil, nil | |
] | |
var currentPlayer: Player = .x | |
var winner: Winner? | |
} | |
public enum Action: ReSwift.Action { | |
case move(location: Int) | |
case reset | |
} | |
open class Reducer { | |
static func ticTacToe(action: ReSwift.Action, state: State?) -> State { | |
var state = state ?? State() | |
state = self.reset(action: action, state: state) | |
state = self.move(action: action, state: state) | |
return state | |
} | |
private static func reset(action: ReSwift.Action, state: State?) -> State { | |
var state = state ?? State() | |
guard let action = action as? Action else { | |
return state | |
} | |
if case .reset = action { | |
state = State() | |
} | |
return state | |
} | |
private static func move(action: ReSwift.Action, state: State?) -> State { | |
var state = self.handlePlayerMove(action: action, state: state) | |
state = self.checkForWinner(action: action, state: state) | |
return state | |
} | |
private static func handlePlayerMove(action: ReSwift.Action, state: State?) -> State { | |
var state = state ?? State() | |
guard let action = action as? Action else { | |
return state | |
} | |
if case let .move(location) = action { | |
if location >= 0 && location < 9 && state.board[location] == nil { | |
state.board[location] = state.currentPlayer | |
switch state.currentPlayer { | |
case .x: | |
state.currentPlayer = .o | |
case .o: | |
state.currentPlayer = .x | |
} | |
} | |
} | |
return state | |
} | |
private static func checkForWinner(action: ReSwift.Action, state: State?) -> State { | |
var state = state ?? State() | |
var winner: Winner? | |
winner = winner ?? self.getWinner(state.board, 0, 1, 2) | |
winner = winner ?? self.getWinner(state.board, 3, 4, 5) | |
winner = winner ?? self.getWinner(state.board, 6, 7, 8) | |
winner = winner ?? self.getWinner(state.board, 0, 3, 6) | |
winner = winner ?? self.getWinner(state.board, 1, 4, 7) | |
winner = winner ?? self.getWinner(state.board, 2, 5, 8) | |
winner = winner ?? self.getWinner(state.board, 0, 4, 8) | |
winner = winner ?? self.getWinner(state.board, 2, 4, 6) | |
state.winner = winner | |
return state | |
} | |
private static func getWinner(_ board: [Player?], _ a: Int, _ b: Int, _ c: Int) -> Winner? { | |
if board[a] == nil || board[b] == nil || board[c] == nil { | |
return nil | |
} | |
if board[a] == board[b] && board[b] == board[c] { | |
return (player: board[a]!, path: [a, b, c]) | |
} | |
return nil | |
} | |
} | |
} |
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
// | |
// ViewController.swift | |
// TicTacToe | |
// | |
// Created by Dannel Albert on 3/22/18. | |
// Copyright © 2018 Dannel Albert. All rights reserved. | |
// | |
import UIKit | |
import ReSwift | |
class ViewController: UIViewController, StoreSubscriber { | |
@IBOutlet weak var button0: UIButton! | |
@IBOutlet weak var button1: UIButton! | |
@IBOutlet weak var button2: UIButton! | |
@IBOutlet weak var button3: UIButton! | |
@IBOutlet weak var button4: UIButton! | |
@IBOutlet weak var button5: UIButton! | |
@IBOutlet weak var button6: UIButton! | |
@IBOutlet weak var button7: UIButton! | |
@IBOutlet weak var button8: UIButton! | |
@IBOutlet weak var playAgainButton: UIButton! | |
@IBOutlet weak var winnerLabel: UILabel! | |
var buttons: [UIButton]! | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
setupButtons() | |
TicTacToe.store.subscribe(self) | |
} | |
func newState(state: TicTacToe.State) { | |
DispatchQueue.main.async { | |
self.render(state: state) | |
} | |
if state.currentPlayer == .o { | |
DispatchQueue.global(qos: .background).async { | |
self.computerMove(state: state) | |
} | |
} | |
} | |
func render(state: TicTacToe.State) { | |
let thereIsAWinner = state.winner != nil | |
let hasMovesLeft = state.board.filter { $0 == nil }.count > 0 | |
playAgainButton.isHidden = !thereIsAWinner && hasMovesLeft | |
winnerLabel.isHidden = !thereIsAWinner && hasMovesLeft | |
for (i, player) in state.board.enumerated() { | |
if let player = player { | |
buttons[i].setTitle(player.rawValue, for: .normal) | |
} else { | |
buttons[i].setTitle(nil, for: .normal) | |
} | |
if state.winner?.path.contains(i) == true { | |
buttons[i].backgroundColor = .green | |
} else { | |
buttons[i].backgroundColor = .lightGray | |
} | |
} | |
if let winner = state.winner { | |
winnerLabel.text = "\(winner.player.rawValue) Won!" | |
} else if !hasMovesLeft { | |
winnerLabel.text = "It's a draw!" | |
} | |
for button in buttons { | |
button.isEnabled = state.currentPlayer == .x && !thereIsAWinner && hasMovesLeft | |
} | |
#if DEBUG | |
renderTextBoard(state: state) | |
#endif | |
} | |
func renderTextBoard(state: TicTacToe.State) { | |
let board = state.board.map { $0?.rawValue ?? " " } | |
print() | |
print(" \(board[0]) | \(board[1]) | \(board[2])") | |
print("-----------") | |
print(" \(board[3]) | \(board[4]) | \(board[5])") | |
print("-----------") | |
print(" \(board[6]) | \(board[7]) | \(board[8])") | |
if let winner = state.winner { | |
print("\(winner.player.rawValue) Won!") | |
} | |
print() | |
} | |
private func setupButtons() { | |
buttons = [ | |
button0, button1, button2, | |
button3, button4, button5, | |
button6, button7, button8 | |
] | |
} | |
func computerMove(state: TicTacToe.State) { | |
if state.winner != nil { | |
return | |
} | |
let emptyLocations = state.board | |
.enumerated() | |
.map { (player: $1, location: $0) } | |
.filter { $0.player == nil } | |
.map { $0.location } | |
if emptyLocations.count == 0 { | |
return | |
} | |
let location = emptyLocations[Int(arc4random_uniform(UInt32(emptyLocations.count)))] | |
TicTacToe.store.dispatch( | |
TicTacToe.Action.move(location: location) | |
) | |
} | |
@IBAction func button0Pressed(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.move(location: 0)) | |
} | |
@IBAction func button1Pressed(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.move(location: 1)) | |
} | |
@IBAction func button2Pressed(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.move(location: 2)) | |
} | |
@IBAction func button3Pressed(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.move(location: 3)) | |
} | |
@IBAction func button4Pressed(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.move(location: 4)) | |
} | |
@IBAction func button5Pressed(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.move(location: 5)) | |
} | |
@IBAction func button6Pressed(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.move(location: 6)) | |
} | |
@IBAction func button7Pressed(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.move(location: 7)) | |
} | |
@IBAction func button8Pressed(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.move(location: 8)) | |
} | |
@IBAction func playAgain(_ sender: Any) { | |
TicTacToe.store.dispatch(TicTacToe.Action.reset) | |
} | |
deinit { | |
TicTacToe.store.unsubscribe(self) | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment