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)
站点信息
- 文章2305
- 用户1336
- 访客11455538
每日一句
Talent without working hard is nothing.
没有努力,天份不代表什么。
没有努力,天份不代表什么。
MySQL 数据库优化
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de
免ROOT实现模拟点击任意位置
Mobaxterm终端神器
CreateProcessW要注意的细节问题
Autonomous NAT Traversal
【教程】win10 彻底卸载edge浏览器
eclipse工程基于Xposed的一个简单Hook
排名前5的开源在线机器学习
Mac OS最简单及(Karabiner)快捷键设置
发一款C++编写的麻将
VMware NAT端口映射外网访问虚拟机linux
独家发布最新可用My-AutoPost——wordpress 采集器
新会员