126 lines
3.5 KiB
Swift
126 lines
3.5 KiB
Swift
//
|
|
// GameController.swift
|
|
// TetrisKit
|
|
//
|
|
// Created by Shadowfacts on 10/13/19.
|
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import Combine
|
|
|
|
public class GameController: ObservableObject {
|
|
|
|
public let width = 10
|
|
public let height = 16
|
|
|
|
@Published public var board: GameBoard
|
|
|
|
@Published public var currentPiece: GamePiece? {
|
|
didSet {
|
|
updateCurrentPieceAtDropPoint()
|
|
}
|
|
}
|
|
@Published public var currentPieceAtDropPoint: GamePiece?
|
|
|
|
public var ended: Bool {
|
|
return (0..<width).first(where: { board[$0, 0] }) != nil
|
|
}
|
|
|
|
public init() {
|
|
self.board = GameBoard(width: width, height: height)
|
|
self.currentPiece = nil
|
|
}
|
|
|
|
public func start() {
|
|
nextPiece()
|
|
}
|
|
|
|
func nextPiece() {
|
|
currentPiece = GamePiece(tetromino: .random())
|
|
currentPiece!.topLeft = ((width - currentPiece!.tiles.count) / 2, 0)
|
|
}
|
|
|
|
func finalizePiece() {
|
|
self.board.set(piece: currentPiece!)
|
|
clearLines()
|
|
nextPiece()
|
|
}
|
|
|
|
func clearLines() {
|
|
let fullRow = Array(repeating: true, count: width)
|
|
var row = height - 1
|
|
while row >= 0 {
|
|
if board.tiles[row] == fullRow {
|
|
board.tiles.remove(at: row)
|
|
}
|
|
row -= 1
|
|
}
|
|
for _ in 0..<height - board.tiles.count {
|
|
board.tiles.insert(Array(repeating: false, count: width), at: 0)
|
|
}
|
|
}
|
|
|
|
public func step() {
|
|
guard let currentPiece = currentPiece else { return }
|
|
let modifiedPiece = currentPiece.moved(by: (0, 1))
|
|
if overlapsAny(modifiedPiece) {
|
|
finalizePiece()
|
|
} else {
|
|
self.currentPiece = modifiedPiece
|
|
}
|
|
}
|
|
|
|
public func rotate(direction: RotationDirection) {
|
|
guard let currentPiece = currentPiece else { return }
|
|
let modifiedPiece = currentPiece.rotated(direction)
|
|
if !overlapsAny(modifiedPiece) {
|
|
self.currentPiece = modifiedPiece
|
|
}
|
|
}
|
|
|
|
public func left() {
|
|
guard let currentPiece = currentPiece else { return }
|
|
let modifiedPiece = currentPiece.moved(by: (-1, 0))
|
|
if !overlapsAny(modifiedPiece) {
|
|
self.currentPiece = modifiedPiece
|
|
}
|
|
}
|
|
|
|
public func right() {
|
|
guard let currentPiece = currentPiece else { return }
|
|
let modifiedPiece = currentPiece.moved(by: (1, 0))
|
|
if !overlapsAny(modifiedPiece) {
|
|
self.currentPiece = modifiedPiece
|
|
}
|
|
}
|
|
|
|
public func drop() {
|
|
currentPiece = currentPieceAtDropPoint
|
|
}
|
|
|
|
func updateCurrentPieceAtDropPoint() {
|
|
guard let currentPiece = currentPiece else { return }
|
|
var prev = currentPiece
|
|
currentPieceAtDropPoint = currentPiece
|
|
while !overlapsAny(currentPieceAtDropPoint!) {
|
|
prev = currentPieceAtDropPoint!
|
|
currentPieceAtDropPoint = currentPieceAtDropPoint!.moved(by: (0, 1))
|
|
}
|
|
currentPieceAtDropPoint = prev
|
|
}
|
|
|
|
public func overlapsAny(_ piece: GamePiece) -> Bool {
|
|
let (left, top) = piece.topLeft
|
|
for y in 0..<piece.tiles.count {
|
|
for x in 0..<piece.tiles.first!.count where piece.tiles[y][x] {
|
|
if top + y >= height || left + x < 0 || left + x >= width || board[left + x, top + y] {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
}
|