privyet/Privyet/Privyet.swift

256 lines
7.3 KiB
Swift

//
// Privyet.swift
// Privyet
//
// Created by Amy Bowersox on 5/24/20.
// Copyright © 2020 Erbosoft Metaverse Design Solutions. All rights reserved.
//
import Foundation
let NumColumns = 10
let NumRows = 20
let StartingColumn = 4
let StartingRow = 0
let PreviewColumn = 12
let PreviewRow = 1
let PointsPerLine = 10
let LevelThreshold = 500
protocol PrivyetDelegate {
// Invoked when the current round of Privyet ends
func gameDidEnd(privyet: Privyet)
// Invoked after a game has begun
func gameDidBegin(privyet: Privyet)
// Invoked when the falling shape has become part of the game board
func gameShapeDidLand(privyet: Privyet)
// Invoked when the falling shape has changed its location
func gameShapeDidMove(privyet: Privyet)
// invoked when the falling shape has changed its location after being dropped
func gameShapeDidDrop(privyet: Privyet)
// invoked when the game has reached a new level
func gameDidLevelUp(privyet: Privyet)
}
class Privyet {
var blockArray: Array2D<Block>
var nextShape: Shape?
var fallingShape: Shape?
var delegate: PrivyetDelegate?
var score = 0
var level = 1
init() {
fallingShape = nil
nextShape = nil
blockArray = Array2D<Block>(columns: NumColumns, rows: NumRows)
}
func beginGame() {
if (nextShape == nil) {
nextShape = Shape.random(startingColumn: PreviewColumn, startingRow: PreviewRow)
}
delegate?.gameDidBegin(privyet: self)
}
func newShape() -> (fallingShape: Shape?, nextShape: Shape?) {
fallingShape = nextShape
nextShape = Shape.random(startingColumn: PreviewColumn, startingRow: PreviewRow)
fallingShape?.moveTo(column: StartingColumn, row: StartingRow)
guard detectIllegalPlacement() == false else {
nextShape = fallingShape
nextShape!.moveTo(column: PreviewColumn, row: PreviewRow)
endGame()
return (nil, nil)
}
return (fallingShape, nextShape)
}
func detectIllegalPlacement() -> Bool {
guard let shape = fallingShape else {
return false
}
for block in shape.blocks {
if block.column < 0 || block.column >= NumColumns || block.row < 0 || block.row >= NumRows {
return true
} else if blockArray[block.column, block.row] != nil {
return true
}
}
return false
}
func settleShape() {
guard let shape = fallingShape else {
return
}
for block in shape.blocks {
blockArray[block.column, block.row] = block
}
fallingShape = nil
delegate?.gameShapeDidLand(privyet: self)
}
func detectTouch() -> Bool {
guard let shape = fallingShape else {
return false
}
for bottomBlock in shape.bottomBlocks {
if bottomBlock.row == NumRows - 1 || blockArray[bottomBlock.column, bottomBlock.row + 1] != nil {
return true
}
}
return false
}
func endGame() {
score = 0
level = 1
delegate?.gameDidEnd(privyet: self)
}
func removeCompletedLines() -> (linesRemoved: Array<Array<Block>>, fallenBlocks: Array<Array<Block>>) {
var removedLines = Array<Array<Block>>()
for row in (1..<NumRows).reversed() {
var rowOfBlocks = Array<Block>()
for column in 0..<NumColumns {
guard let block = blockArray[column, row] else {
continue
}
rowOfBlocks.append(block)
}
if rowOfBlocks.count == NumColumns {
removedLines.append(rowOfBlocks)
for block in rowOfBlocks {
blockArray[block.column, block.row] = nil
}
}
}
if removedLines.count == 0 {
return ([], [])
}
let pointsEarned = removedLines.count * PointsPerLine * level
score += pointsEarned
if score >= level * LevelThreshold {
level += 1
delegate?.gameDidLevelUp(privyet: self)
}
var fallenBlocks = Array<Array<Block>>()
for column in 0..<NumColumns {
var fallenBlocksArray = Array<Block>()
for row in (1..<removedLines[0][0].row).reversed() {
guard let block = blockArray[column, row] else {
continue
}
var newRow = row
while (newRow < NumRows - 1 && blockArray[column, newRow + 1] == nil) {
newRow += 1
}
block.row = newRow
blockArray[column, row] = nil
blockArray[column, newRow] = block
fallenBlocksArray.append(block)
}
if fallenBlocksArray.count > 0 {
fallenBlocks.append(fallenBlocksArray)
}
}
return (removedLines, fallenBlocks)
}
func removeAllBlocks() -> Array<Array<Block>> {
var allBlocks = Array<Array<Block>>()
for row in 0..<NumRows {
var rowOfBlocks = Array<Block>()
for column in 0..<NumColumns {
guard let block = blockArray[column, row] else {
continue
}
rowOfBlocks.append(block)
blockArray[column, row] = nil
}
allBlocks.append(rowOfBlocks)
}
return allBlocks
}
func dropShape() {
guard let shape = fallingShape else {
return
}
while detectIllegalPlacement() == false {
shape.lowerShapeByOneRow()
}
shape.raiseShapeByOneRow()
delegate?.gameShapeDidDrop(privyet: self)
}
func letShapeFall() {
guard let shape = fallingShape else {
return
}
shape.lowerShapeByOneRow()
if detectIllegalPlacement() {
shape.raiseShapeByOneRow()
if detectIllegalPlacement() {
endGame()
} else {
settleShape()
}
} else {
delegate?.gameShapeDidMove(privyet: self)
if detectTouch() {
settleShape()
}
}
}
func rotateShape() {
guard let shape = fallingShape else {
return
}
shape.rotateClockwise()
guard detectIllegalPlacement() == false else {
shape.rotateCounterClockwise()
return
}
delegate?.gameShapeDidMove(privyet: self)
}
func moveShapeLeft() {
guard let shape = fallingShape else {
return
}
shape.shiftLeftByOneColumn()
guard detectIllegalPlacement() == false else {
shape.shiftRightByOneColumn()
return
}
delegate?.gameShapeDidMove(privyet: self)
}
func moveShapeRight() {
guard let shape = fallingShape else {
return
}
shape.shiftRightByOneColumn()
guard detectIllegalPlacement() == false else {
shape.shiftLeftByOneColumn()
return
}
delegate?.gameShapeDidMove(privyet: self)
}
}