Swift就是Apple在2014年所发布的最新的编程语言啦,是用来编写苹果设备app的官方编程语言。将会慢慢的代替掉现今的Object-c,成为ios编程的主要语言。 最主要的是,Swift语言的门槛比以前的编程语言更低,很可能以后会进入一个全民编写”app”的时代,所以用自己的课余时间来学习下,不用多久,就会拥有自己创作的app 啦。加油各位!
下地地址:swift-2048-master
下地地址:swift-2048-master
import UIKit /// A protocol that establishes a way for the game model to communicate with its parent view controller. @class_protocol protocol GameModelProtocol { func scoreChanged(score: Int) func moveOneTile(from: (Int, Int), to: (Int, Int), value: Int) func moveTwoTiles(from: ((Int, Int), (Int, Int)), to: (Int, Int), value: Int) func insertTile(location: (Int, Int), value: Int) } /// A class representing the game state and game logic for swift-2048. It is owned by a NumberTileGame view controller. class GameModel: NSObject { let dimension: Int let threshold: Int var score: Int = 0 { didSet { delegate.scoreChanged(score) } } var gameboard: SquareGameboard// This really should be unowned/weak. But there is currently a bug that causes the app to crash whenever the delegate // is accessed unless the delegate type is a specific class (rather than a protocol). let delegate: GameModelProtocol var queue: MoveCommand[] var timer: NSTimer let maxCommands = 100 let queueDelay = 0.3 init(dimension d: Int, threshold t: Int, delegate: GameModelProtocol) { dimension = d threshold = t self.delegate = delegate queue = MoveCommand[]() timer = NSTimer() gameboard = SquareGameboard(dimension: d, initialValue: .Empty) super.init() } /// Reset the game state. func reset() { score = 0 gameboard.setAll(.Empty) queue.removeAll(keepCapacity: true) timer.invalidate() } /// Order the game model to perform a move (because the user swiped their finger). The queue enforces a delay of a few /// milliseconds between each move. func queueMove(direction: MoveDirection, completion: (Bool) -> ()) { if queue.count > maxCommands { // Queue is wedged. This should actually never happen in practice. return } let command = MoveCommand(d: direction, c: completion) queue.append(command) if (!timer.valid) { // Timer isn't running, so fire the event immediately timerFired(timer) } } //------------------------------------------------------------------------------------------------------------------// /// Inform the game model that the move delay timer fired. Once the timer fires, the game model tries to execute a /// single move that changes the game state. func timerFired(timer: NSTimer) { if queue.count == 0 { return } // Go through the queue until a valid command is run or the queue is empty var changed = false while queue.count > 0 { let command = queue[0] queue.removeAtIndex(0) changed = performMove(command.direction) command.completion(changed) if changed { // If the command doesn't change anything, we immediately run the next one break } } if changed { self.timer = NSTimer.scheduledTimerWithTimeInterval(queueDelay, target: self, selector: Selector("timerFired:"), userInfo: nil, repeats: false) } } //------------------------------------------------------------------------------------------------------------------// /// Insert a tile with a given value at a position upon the gameboard. func insertTile(pos: (Int, Int), value: Int) { let (x, y) = pos switch gameboard[x, y] { case .Empty: gameboard[x, y] = TileObject.Tile(value: value) delegate.insertTile(pos, value: value) case .Tile: break } } /// Insert a tile with a given value at a random open position upon the gameboard. func insertTileAtRandomLocation(value: Int) { let openSpots = gameboardEmptySpots() if openSpots.count == 0 { // No more open spots; don't even bother return } // Randomly select an open spot, and put a new tile there let idx = Int(arc4random_uniform(UInt32(openSpots.count-1))) let (x, y) = openSpots[idx] insertTile((x, y), value: value) } /// Return a list of tuples describing the coordinates of empty spots remaining on the gameboard. func gameboardEmptySpots() -> (Int, Int)[] { var buffer = Array<(Int, Int)>() for i in 0..dimension { for j in 0..dimension { switch gameboard[i, j] { case .Empty: buffer += (i, j) case .Tile: break } } } return buffer } func gameboardFull() -> Bool { return gameboardEmptySpots().count == 0 } //------------------------------------------------------------------------------------------------------------------// func userHasLost() -> Bool { if !gameboardFull() { // Player can't lose before filling up the board return false } func tileBelowHasSameValue(loc: (Int, Int), value: Int) -> Bool { let (x, y) = loc if y == dimension-1 { return false } switch gameboard[x, y+1] { case let .Tile(v): return v == value default: return false } } func tileToRightHasSameValue(loc: (Int, Int), value: Int) -> Bool { let (x, y) = loc if x == dimension-1 { return false } switch gameboard[x+1, y] { case let .Tile(v): return v == value default: return false } } // Run through all the tiles and check for possible moves for i in 0..dimension { for j in 0..dimension { switch gameboard[i, j] { case .Empty: assert(false, "Gameboard reported itself as full, but we still found an empty tile. This is a logic error.") case let .Tile(v): if tileBelowHasSameValue((i, j), v) || tileToRightHasSameValue((i, j), v) { return false } } } } return true } func userHasWon() -> (Bool, (Int, Int)?) { for i in 0..dimension { for j in 0..dimension { // Look for a tile with the winning score or greater switch gameboard[i, j] { case let .Tile(v) where v >= threshold: return (true, (i, j)) default: continue } } } return (false, nil) } //------------------------------------------------------------------------------------------------------------------// // Perform all calculations and update state for a single move. func performMove(direction: MoveDirection) -> Bool { // Prepare the generator closure. This closure differs in behavior depending on the direction of the move. It is // used by the method to generate a list of tiles which should be modified. Depending on the direction this list // may represent a single row or a single column, in either direction. let coordinateGenerator: (Int) -> (Int, Int)[] = { (iteration: Int) -> (Int, Int)[] in let buffer = Array<(Int, Int)>(count:self.dimension, repeatedValue: (0, 0)) for i in 0..self.dimension { switch direction { case .Up: buffer[i] = (i, iteration) case .Down: buffer[i] = (self.dimension - i - 1, iteration) case .Left: buffer[i] = (iteration, i) case .Right: buffer[i] = (iteration, self.dimension - i - 1) } } return buffer } var atLeastOneMove = false for i in 0..dimension { // Get the list of coords let coords = coordinateGenerator(i) // Get the corresponding list of tiles let tiles = coords.map() { (c: (Int, Int)) -> TileObject in let (x, y) = c return self.gameboard[x, y] } // Perform the operation let orders = merge(tiles) atLeastOneMove = orders.count > 0 ? true : atLeastOneMove // Write back the results for object in orders { switch object { case let MoveOrder.SingleMoveOrder(s, d, v, wasMerge): // Perform a single-tile move let (sx, sy) = coords[s] let (dx, dy) = coords[d] if wasMerge { score += v } gameboard[sx, sy] = TileObject.Empty gameboard[dx, dy] = TileObject.Tile(value: v) delegate.moveOneTile(coords[s], to: coords[d], value: v) case let MoveOrder.DoubleMoveOrder(s1, s2, d, v): // Perform a simultaneous two-tile move let (s1x, s1y) = coords[s1] let (s2x, s2y) = coords[s2] let (dx, dy) = coords[d] score += v gameboard[s1x, s1y] = TileObject.Empty gameboard[s2x, s2y] = TileObject.Empty gameboard[dx, dy] = TileObject.Tile(value: v) delegate.moveTwoTiles((coords[s1], coords[s2]), to: coords[d], value: v) } } } return atLeastOneMove } //------------------------------------------------------------------------------------------------------------------// /// When computing the effects of a move upon a row of tiles, calculate and return a list of ActionTokens /// corresponding to any moves necessary to remove interstital space. For example, |[2][ ][ ][4]| will become /// |[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; } /// When computing the effects of a move upon a row of tiles, calculate and return an updated list of ActionTokens /// corresponding to any merges that should take place. This method collapses adjacent tiles of equal value, but each /// tile can take part in at most one collapse per move. For example, |[1][1][1][2][2]| will become |[2][1][4]|. 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 } /// When computing the effects of a move upon a row of tiles, take a list of ActionTokens prepared by the condense() /// and convert() methods and convert them into MoveOrders that can be fed back to the delegate. 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, wasMerge: false)) case let .SingleCombine(s, v): moveBuffer.append(MoveOrder.SingleMoveOrder(source: s, destination: idx, value: v, wasMerge: true)) 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. func merge(group: TileObject[]) -> MoveOrder[] { // Calculation takes place in three steps: // 1. Calculate the moves necessary to produce the same tiles, but without any interstital space. // 2. Take the above, and calculate the moves necessary to collapse adjacent tiles of equal value. // 3. Take the above, and convert into MoveOrders that provide all necessary information to the delegate. return convert(collapse(condense(group))) } }
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2300
- 用户1336
- 访客10861715
每日一句
True success inspires others to act.
真正的成功是激励他人行动。
真正的成功是激励他人行动。
语法错误: 意外的令牌“标识符”
全面理解Gradle - 定义Task
Motrix全能下载工具 (支持 BT / 磁力链 / 百度网盘)
谷歌Pixel正在开始起飞?
获取ElementUI Table排序后的数据
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is
亲测!虚拟机VirtualBox安装MAC OS 10.12图文教程
华为手机app闪退重启界面清空log日志问题
android ndk开发之asm/page.h: not found
手机屏幕碎了怎么备份操作?
免ROOT实现模拟点击任意位置
新手必看修改DSDT教程
thinkpad t470p装黑苹果系统10.13.2
新会员