Skip to content

Commit

Permalink
init check in
Browse files Browse the repository at this point in the history
  • Loading branch information
robin committed Aug 4, 2016
1 parent 871c9c6 commit 90ea109
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.DS_Store

# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
Expand Down
360 changes: 360 additions & 0 deletions 2048.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@

//: 2048 Playground - by Lu Yibin

import UIKit
import PlaygroundSupport
import GameplayKit

/// Movement orientation
enum Orientation {
case vertical
case horizon
}

/// Movement direction
enum Direction : Int {
case forward = 1
case backward = -1
}

/// Position of tiles in the board
struct Position : Equatable{
let x : Int
let y : Int

/// find previouse position according to the direction and orientation
func previousPosition(direction:Direction, orientation:Orientation) -> Position {
switch orientation {
case .vertical:
return Position(x: x, y: y - direction.rawValue)
case .horizon:
return Position(x: x - direction.rawValue, y: y)
}
}
}

func ==(lhs: Position, rhs: Position) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}

/// Style configuration of the game
struct Style {
let boardBackgroundColor = #colorLiteral(red: 0.6823529412, green: 0.6235294118, blue: 0.5607843137, alpha: 1)
let emptyTileBackgroundColor = #colorLiteral(red: 0.7607843137, green: 0.7058823529, blue: 0.6392156863, alpha: 1)
let tileBackgroundColors = [
#colorLiteral(red: 0.7529411765, green: 0.7098039216, blue: 0.6509803922, alpha: 1), #colorLiteral(red: 0.9254901961, green: 0.8745098039, blue: 0.8196078431, alpha: 1), #colorLiteral(red: 0.9098039216, green: 0.8549019608, blue: 0.7490196078, alpha: 1), #colorLiteral(red: 0.9333333333, green: 0.6392156863, blue: 0.3960784314, alpha: 1),
#colorLiteral(red: 0.9019607843, green: 0.4784313725, blue: 0.262745098, alpha: 1), #colorLiteral(red: 0.9764705882, green: 0.3843137255, blue: 0.2980392157, alpha: 1), #colorLiteral(red: 0.8901960784, green: 0.2549019608, blue: 0.1764705882, alpha: 1), #colorLiteral(red: 0.9450980392, green: 0.8235294118, blue: 0.3568627451, alpha: 1),
#colorLiteral(red: 0.9333333333, green: 0.7921568627, blue: 0.231372549, alpha: 1), #colorLiteral(red: 0.8745098039, green: 0.7137254902, blue: 0.1333333333, alpha: 1), #colorLiteral(red: 0.9137254902, green: 0.7333333333, blue: 0.1921568627, alpha: 1), #colorLiteral(red: 0.9098039216, green: 0.737254902, blue: 0.03921568627, alpha: 1)
]
let tileForgroundColors = [
#colorLiteral(red: 0.3882352941, green: 0.3647058824, blue: 0.3176470588, alpha: 1),
UIColor.white()
]

/// tile background color according to the value of the tile
func tileBackgroundColor(value:Int) -> CGColor {
return value < tileBackgroundColors.count ? tileBackgroundColors[value].cgColor : tileBackgroundColors.last!.cgColor
}

/// tile text color according to the value of the tile
func tileForgroundColor(value:Int) -> UIColor {
return value > 2 ? tileForgroundColors[1] : tileForgroundColors[0]
}
}

/// size configuration of the board
struct BoardSizeConfig {
let tileNumber = 4
let tileCount = 16
let boardSize = CGSize(width: 290, height: 290)
let tileSize = CGSize(width: 60, height: 60)
let borderSize = CGSize(width: 10, height: 10)
}

let style = Style()
let config = BoardSizeConfig()

class Tile : Equatable {
var value : Int = 0 {
didSet {
updateView()
}
}
var valueText : String {
return value == 0 ? "" : "\(1<<value)"
}

var isEmpty : Bool {
return value == 0
}

let view : UILabel = UILabel(frame: .zero)
var position : Position {
didSet {
guard let board = self.board else {
return
}
let point = board.pointAt(position: self.position)

self.topConstraint?.constant = point.y
self.leftConstraint?.constant = point.x
}
}

var board : Board?
var topConstraint : NSLayoutConstraint?
var leftConstraint : NSLayoutConstraint?

init(value: Int, position : Position = Position(x: 0, y: 0) ){
self.position = position
view.font = UIFont.boldSystemFont(ofSize: 30)
view.textColor = UIColor.white()
view.textAlignment = .center
view.layer.cornerRadius = 3
view.layer.backgroundColor = UIColor.blue().cgColor
view.translatesAutoresizingMaskIntoConstraints = false
self.value = value
updateView()
}

func updateView() {
view.text = self.valueText
let len = view.text?.characters.count
if len > 4 {
view.font = UIFont.boldSystemFont(ofSize: 18)
} else if len > 3 {
view.font = UIFont.boldSystemFont(ofSize: 20)
}
view.layer.backgroundColor = style.tileBackgroundColor(value: value)
view.textColor = style.tileForgroundColor(value: value)
}

func moveTo(position:Position) {
self.position = position
}

func mergeTo(position:Position) {
moveTo(position: position)
self.value += 1
}

func addTo(board:Board) {
guard self.board == nil else {
return
}
self.board = board
let boardView = board.boardView
boardView.addSubview(view)
view.widthAnchor.constraint(equalToConstant: config.tileSize.width).isActive = true
view.heightAnchor.constraint(equalToConstant: config.tileSize.height).isActive = true

let point = board.pointAt(position:self.position)
topConstraint = view.topAnchor.constraint(equalTo: boardView.topAnchor, constant: point.y)
leftConstraint = view.leftAnchor.constraint(equalTo: boardView.leftAnchor, constant: point.x)
topConstraint?.isActive = true
leftConstraint?.isActive = true
}

func removeFromBoard() {
self.view.removeFromSuperview()
}

func createPreviousEmptyTile(direction:Direction, orientation:Orientation) -> Tile {
let pos = self.position.previousPosition(direction: direction, orientation: orientation)
return Tile(value: 0, position: pos)
}
}

func ==(lhs: Tile, rhs: Tile) -> Bool {
return lhs.value == rhs.value && lhs.position == rhs.position
}

class Board {
let boardView : UIView
var tileArray = [Tile]()

init() {
boardView = UIView(frame: .zero)
boardView.backgroundColor = style.boardBackgroundColor
boardView.layer.cornerRadius = 6
boardView.translatesAutoresizingMaskIntoConstraints = false
}

func pointAt(x:Int, y:Int) -> CGPoint {
let offsetX = config.borderSize.width
let offsetY = config.borderSize.height
let width = config.tileSize.width + config.borderSize.width
let height = config.tileSize.height + config.borderSize.height
return CGPoint(x: offsetX + width * CGFloat(x), y: offsetY + height * CGFloat(y))
}

func pointAt(position:Position) -> CGPoint {
return pointAt(x: position.x, y: position.y)
}

func addTo(view : UIView){
view.addSubview(self.boardView)
boardView.widthAnchor.constraint(equalToConstant: config.boardSize.width).isActive = true
boardView.heightAnchor.constraint(equalTo: boardView.widthAnchor).isActive = true
boardView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
boardView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}

func gameOver() {
print("game over")
}

func add(tile:Tile) {
tile.addTo(board: self)
tileArray.append(tile)
}

func generateTile() {
guard tileArray.count <= config.tileCount else {
print("no space available")
return
}

var tileList : [(Int,Int)?] = Array(repeating: nil, count: config.tileCount)
for x in 0..<config.tileNumber {
for y in 0..<config.tileNumber {
tileList[x+y*config.tileNumber] = (x,y)
}
}
for tile in tileArray {
tileList[tile.position.x + tile.position.y * config.tileNumber] = nil
}
let remain = tileList.flatMap {$0}
let random = arc4random_uniform(UInt32(remain.count))
let value = Int(arc4random_uniform(3)/2)+1
let (x, y) = remain[Int(random)]
let tile = Tile(value: value, position: Position(x: x, y: y))

tile.addTo(board: self)
tileArray.append(tile)
}

func buildBoard() {
for i in 0..<4 {
for j in 0..<4 {
let layer = CALayer()
layer.frame = CGRect(origin: pointAt(x: i, y: j), size: config.tileSize)
layer.backgroundColor = style.emptyTileBackgroundColor.cgColor
layer.cornerRadius = 3
boardView.layer.addSublayer(layer)
}
}
generateTile()
generateTile()
}

func removeFromTileArray(tile:Tile) {
if let idx = tileArray.index(where: {$0 == tile}) {
tileArray.remove(at: idx)
tile.removeFromBoard()
}
}
func checkMovement(direction:Direction, orientation:Orientation) -> Bool {
var moved = false
var tileList = [Tile]()
for y in 0..<config.tileNumber {
for x in 0..<config.tileNumber {
let tile = Tile(value: 0, position: Position(x: x, y: y))
tileList.append(tile)
}
}
for tile in tileArray {
tileList[tile.position.x + tile.position.y * config.tileNumber] = tile
}
var lastZeroTile : Tile? = nil
var lastMergableTile : Tile? = nil
for i in 0..<config.tileNumber {
lastZeroTile = nil
lastMergableTile = nil
for j in 0..<config.tileNumber {
let temp = direction == .forward ? (config.tileNumber - 1) - j : j
let x = orientation == .horizon ? temp : i
let y = orientation == .horizon ? i : temp
let tile = tileList[x + y * config.tileNumber]
if !tile.isEmpty {
if let mergableTile = lastMergableTile, mergableTile.value == tile.value {
tile.mergeTo(position: mergableTile.position)
removeFromTileArray(tile: mergableTile)
lastMergableTile = nil
lastZeroTile = tile.createPreviousEmptyTile(direction: direction, orientation: orientation)
moved = true
continue
}
if let zeroTile = lastZeroTile {
tile.moveTo(position: zeroTile.position)
lastZeroTile = tile.createPreviousEmptyTile(direction: direction, orientation: orientation)
moved = true
}
lastMergableTile = tile
} else {
if lastZeroTile == nil {
lastZeroTile = tile
}
}
}
}
return moved
}

func moveTile(direction: Direction, orientation: Orientation) {
let moved = checkMovement(direction: direction, orientation: orientation)
UIView.animate(withDuration: 0.1, animations: {
self.boardView.layoutIfNeeded()
}) { (_) in
if moved {
self.generateTile()
}
}
}
}

class GameViewController : UIViewController {
let board = Board()


@IBAction func swipeRight(sender:AnyObject?) {
board.moveTile(direction: .forward, orientation: .horizon)
}

@IBAction func swipeLeft(sender:AnyObject?) {
board.moveTile(direction: .backward, orientation: .horizon)
}

@IBAction func swipeUp(sender:AnyObject?) {
board.moveTile(direction: .backward, orientation: .vertical)
}

@IBAction func swipeDown(sender:AnyObject?) {
board.moveTile(direction: .forward, orientation: .vertical)
}

override func viewDidLoad() {
if let view = self.view {
view.backgroundColor = UIColor(white: 1.0, alpha: 1)
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

let gesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeRight))
view.addGestureRecognizer(gesture)
let gestureLeft = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeft))
gestureLeft.direction = .left
view.addGestureRecognizer(gestureLeft)
let gestureUp = UISwipeGestureRecognizer(target: self, action: #selector(swipeUp))
gestureUp.direction = .up
view.addGestureRecognizer(gestureUp)
let gestureDown = UISwipeGestureRecognizer(target: self, action: #selector(swipeDown))
gestureDown.direction = .down
view.addGestureRecognizer(gestureDown)

board.addTo(view: view)
board.buildBoard()
}
}
}

let controller = GameViewController()
PlaygroundPage.current.liveView = controller
2 changes: 2 additions & 0 deletions 2048.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='ios' display-mode='raw'/>
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# 2048_Playground
A swift playground to run the game 2048
A swift playground of the game 2048. It’s written in Swift 3 and can be run in Xcode 8 beta 3 or Swift Playground of iOS 10 beta 3.

0 comments on commit 90ea109

Please sign in to comment.