Skip to content

Commit

Permalink
Initial developer commit
Browse files Browse the repository at this point in the history
Changes:
- Created project
- Ported over the core of the game model and added unit tests
  • Loading branch information
austinzheng committed Jun 4, 2014
1 parent ede4cff commit 6703d43
Show file tree
Hide file tree
Showing 6 changed files with 714 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
swift-2048.xcodeproj/xcuserdata/
28 changes: 28 additions & 0 deletions swift-2048.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
1AC563E8193DCE6000402286 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1AC563E6193DCE6000402286 /* Main.storyboard */; };
1AC563EA193DCE6000402286 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1AC563E9193DCE6000402286 /* Images.xcassets */; };
1AC563F6193DCE6000402286 /* swift_2048Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC563F5193DCE6000402286 /* swift_2048Tests.swift */; };
1AE339B2193DCF110081FFA4 /* GameModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE339B1193DCF110081FFA4 /* GameModel.swift */; };
1AE339B4193DF5FF0081FFA4 /* ModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE339B3193DF5FF0081FFA4 /* ModelTests.swift */; };
1AE339B5193DF6540081FFA4 /* GameModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE339B1193DCF110081FFA4 /* GameModel.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -34,6 +37,8 @@
1AC563EF193DCE6000402286 /* swift-2048Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "swift-2048Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
1AC563F4193DCE6000402286 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
1AC563F5193DCE6000402286 /* swift_2048Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = swift_2048Tests.swift; sourceTree = "<group>"; };
1AE339B1193DCF110081FFA4 /* GameModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameModel.swift; path = Models/GameModel.swift; sourceTree = "<group>"; };
1AE339B3193DF5FF0081FFA4 /* ModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModelTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -80,6 +85,8 @@
1AC563E6193DCE6000402286 /* Main.storyboard */,
1AC563E9193DCE6000402286 /* Images.xcassets */,
1AC563E0193DCE6000402286 /* Supporting Files */,
1AE339AF193DCE990081FFA4 /* Models */,
1AE339B0193DCEAE0081FFA4 /* Views */,
);
path = "swift-2048";
sourceTree = "<group>";
Expand All @@ -97,6 +104,7 @@
children = (
1AC563F5193DCE6000402286 /* swift_2048Tests.swift */,
1AC563F3193DCE6000402286 /* Supporting Files */,
1AE339B3193DF5FF0081FFA4 /* ModelTests.swift */,
);
path = "swift-2048Tests";
sourceTree = "<group>";
Expand All @@ -109,6 +117,21 @@
name = "Supporting Files";
sourceTree = "<group>";
};
1AE339AF193DCE990081FFA4 /* Models */ = {
isa = PBXGroup;
children = (
1AE339B1193DCF110081FFA4 /* GameModel.swift */,
);
name = Models;
sourceTree = "<group>";
};
1AE339B0193DCEAE0081FFA4 /* Views */ = {
isa = PBXGroup;
children = (
);
name = Views;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -210,6 +233,7 @@
files = (
1AC563E5193DCE6000402286 /* ViewController.swift in Sources */,
1AC563E3193DCE6000402286 /* AppDelegate.swift in Sources */,
1AE339B2193DCF110081FFA4 /* GameModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -218,6 +242,8 @@
buildActionMask = 2147483647;
files = (
1AC563F6193DCE6000402286 /* swift_2048Tests.swift in Sources */,
1AE339B4193DF5FF0081FFA4 /* ModelTests.swift in Sources */,
1AE339B5193DF6540081FFA4 /* GameModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -399,6 +425,7 @@
1AC563FB193DCE6000402286 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1AC563FC193DCE6000402286 /* Build configuration list for PBXNativeTarget "swift-2048Tests" */ = {
isa = XCConfigurationList;
Expand All @@ -407,6 +434,7 @@
1AC563FE193DCE6000402286 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
7 changes: 4 additions & 3 deletions swift-2048/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6162" systemVersion="14A238h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6154.17" systemVersion="13D65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6160"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6153.11"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="swift_2048" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
Expand All @@ -16,6 +16,7 @@
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
Expand Down
173 changes: 173 additions & 0 deletions swift-2048/Models/GameModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
//
// GameModel.swift
// swift-2048
//
// Created by Austin Zheng on 6/3/14.
// Copyright (c) 2014 Austin Zheng. All rights reserved.
//

import UIKit

// Represents a 'move order'
enum MoveOrder {
case SingleMoveOrder(source: Int, destination: Int, value: Int)
case DoubleMoveOrder(firstSource: Int, secondSource: Int, destination: Int, value: Int)
}

// Represents an object in the tile grid
enum TileObject {
case Empty
case Tile(value: Int)
}

// Represents an action applied to a tile; used to generate move orders
enum ActionToken {
case NoAction(source: Int, value: Int)
case Move(source: Int, value: Int)
case SingleCombine(source: Int, value: Int)
case DoubleCombine(source: Int, second: Int, value: Int)

// Get the 'value', regardless of the specific type
func getValue() -> Int {
switch self {
case let .NoAction(_, v): return v
case let .Move(_, v): return v
case let .SingleCombine(_, v): return v
case let .DoubleCombine(_, _, v): return v
}
}
// Get the 'source', regardless of the specific type
func getSource() -> Int {
switch self {
case let .NoAction(s, _): return s
case let .Move(s, _): return s
case let .SingleCombine(s, _): return s
case let .DoubleCombine(s, _, _): return s
}
}
}

class GameModel: NSObject {
let dimension: Int
let threshold: Int

var score: Int
var gameboard: TileObject[][]

init(dimension: Int, threshold: Int) {
self.dimension = dimension
self.threshold = threshold
// TODO: delegate

score = 0
// Initialize the gameboard. Not sure how to do this more efficiently
gameboard = TileObject[][]()
for i in 0...dimension {
gameboard.append(TileObject[](count:dimension, repeatedValue:TileObject.Empty))
}
super.init()
}

func reset() {
// TODO: reset
self.score = 0
}

//------------------------------------------------------------------------------------------------------------------//

// Remove interstital space (e.g. |[2][-][-][4]| becomes |[2][4]|)
func condense(group: TileObject[]) -> ActionToken[] {
var tokenBuffer = ActionToken[]()
for (idx, tile) in enumerate(group) {
// Go through all the tiles in 'group'. When we see a tile 'out of place', create a corresponding ActionToken.
switch tile {
case let .Tile(value) where tokenBuffer.count == idx:
tokenBuffer.append(ActionToken.NoAction(source: idx, value: value))
case let .Tile(value):
tokenBuffer.append(ActionToken.Move(source: idx, value: value))
default:
break
}
}
return tokenBuffer;
}

// Collapse adjacent tiles of equal value
func collapse(group: ActionToken[]) -> ActionToken[] {
func quiescentTileStillQuiescent(inputPosition: Int, outputLength: Int, originalPosition: Int) -> Bool {
// Return whether or not a 'NoAction' token still represents an unmoved tile
return (inputPosition == outputLength) && (originalPosition == inputPosition)
}

var tokenBuffer = ActionToken[]()
var skipNext = false
for (idx, token) in enumerate(group) {
if skipNext {
// Prior iteration handled a merge. So skip this iteration.
skipNext = false
continue
}
switch token {
case .SingleCombine:
assert(false, "Cannot have single combine token in input")
case .DoubleCombine:
assert(false, "Cannot have double combine token in input")
case let .NoAction(s, v)
where (idx < group.count-1
&& v == group[idx+1].getValue()
&& quiescentTileStillQuiescent(idx, tokenBuffer.count, s)):
// This tile hasn't moved yet, but matches the next tile. This is a single merge
// The last tile is *not* eligible for a merge
let next = group[idx+1]
let nv = v + group[idx+1].getValue()
skipNext = true
tokenBuffer.append(ActionToken.SingleCombine(source: next.getSource(), value: nv))
case let t where (idx < group.count-1 && t.getValue() == group[idx+1].getValue()):
// This tile has moved, and matches the next tile. This is a double merge
// (The tile may either have moved prevously, or the tile might have moved as a result of a previous merge)
// The last tile is *not* eligible for a merge
let next = group[idx+1]
let nv = t.getValue() + group[idx+1].getValue()
skipNext = true
tokenBuffer.append(ActionToken.DoubleCombine(source: t.getSource(), second: next.getSource(), value: nv))
case let .NoAction(s, v) where !quiescentTileStillQuiescent(idx, tokenBuffer.count, s):
// A tile that didn't move before has moved (first cond.), or there was a previous merge (second cond.)
tokenBuffer.append(ActionToken.Move(source: s, value: v))
case let .NoAction(s, v):
// A tile that didn't move before still hasn't moved
tokenBuffer.append(ActionToken.NoAction(source: s, value: v))
case let .Move(s, v):
// Propagate a move
tokenBuffer.append(ActionToken.Move(source: s, value: v))
default:
// Don't do anything
break
}
}
return tokenBuffer
}

// Convert all action tokens into move orders
func convert(group: ActionToken[]) -> MoveOrder[] {
var moveBuffer = MoveOrder[]()
for (idx, t) in enumerate(group) {
switch t {
case let .Move(s, v):
moveBuffer.append(MoveOrder.SingleMoveOrder(source: s, destination: idx, value: v))
case let .SingleCombine(s, v):
moveBuffer.append(MoveOrder.SingleMoveOrder(source: s, destination: idx, value: v))
case let .DoubleCombine(s1, s2, v):
moveBuffer.append(MoveOrder.DoubleMoveOrder(firstSource: s1, secondSource: s2, destination: idx, value: v))
default:
// Don't do anything
break
}
}
return moveBuffer
}

// Given an array of TileObjects, perform a collapse and create an array of move orders that can be fed to the view
func merge(group: TileObject[]) -> MoveOrder[] {
return convert(collapse(condense(group)))
}
}
8 changes: 0 additions & 8 deletions swift-2048/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@ class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}


}

Loading

0 comments on commit 6703d43

Please sign in to comment.