// // 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 = 3 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 bucket: Array2D var nextShape: Shape? var fallingShape: Shape? var delegate: PrivyetDelegate? var score = 0 var level = 1 init() { fallingShape = nil nextShape = nil bucket = Array2D(columns: NumColumns, rows: NumRows) } // Starts the game by initializing game data func beginGame() { if (nextShape == nil) { nextShape = Shape.random(startingColumn: PreviewColumn, startingRow: PreviewRow) } delegate?.gameDidBegin(privyet: self) } // Advances the current falling shape and the "next" shape. Returns both these shapes as a tuple. 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) } // Returns true if the falling shape is in an "illegal" placement (outside bucket bounds or "colliding" // with existing blocks in the bucket). 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 bucket[block.column, block.row] != nil { return true } } return false } // "Settle" the falling shape by transferring its blocks to the bucket. func settleShape() { guard let shape = fallingShape else { return } for block in shape.blocks { bucket[block.column, block.row] = block } fallingShape = nil delegate?.gameShapeDidLand(privyet: self) } // Returns true if the falling block is "landing" (at the bottom of the bucket or directly above existing blocks) func detectTouch() -> Bool { guard let shape = fallingShape else { return false } for bottomBlock in shape.bottomBlocks { if bottomBlock.row == NumRows - 1 || bucket[bottomBlock.column, bottomBlock.row + 1] != nil { return true } } return false } // Ends the current game func endGame() { score = 0 level = 1 delegate?.gameDidEnd(privyet: self) } // Detect and remove completed lines. Returns a tuple of two arrays of arrays: // linesRemoved - The blocks that will be removed as a result of completed lines. // fallenBlocks - The blocks that will drop to a lower line in the bucket when the above blocks are removed. // If no lines are to be removed, returns a pair of empty lists. func removeCompletedLines() -> (linesRemoved: Array>, fallenBlocks: Array>) { var removedLines = Array>() for row in (1..() for column in 0..= level * LevelThreshold { level += 1 delegate?.gameDidLevelUp(privyet: self) } var fallenBlocks = Array>() for column in 0..() for row in (1.. 0 { fallenBlocks.append(fallenBlocksArray) } } return (removedLines, fallenBlocks) } // Removes all blocks from the bucket (at end of game). Returns all the removed blocks. func removeAllBlocks() -> Array> { var allBlocks = Array>() for row in 0..() for column in 0..