Last active
November 30, 2018 11:17
-
-
Save troughton/9fdecf4dc09b0406c444c28f40f80d43 to your computer and use it in GitHub Desktop.
Swift Tromino Algorithm - Windows Test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Swift | |
print("Hello") | |
struct Position : Hashable { | |
let x : Int | |
let y : Int | |
var hashValue: Int { | |
return 31 &* x &+ y | |
} | |
} | |
func +(lhs: Position, rhs: Position) -> Position { | |
return Position(x: lhs.x + rhs.x, y: lhs.y + rhs.y) | |
} | |
func -(lhs: Position, rhs: Position) -> Position { | |
return Position(x: lhs.x - rhs.x, y: lhs.y - rhs.y) | |
} | |
func ==(lhs: Position, rhs: Position) -> Bool { | |
return lhs.x == rhs.x && lhs.y == rhs.y | |
} | |
struct Tromino : Hashable, Equatable, CustomStringConvertible { | |
enum Orientation { | |
///Space at bottom right | |
case UpperLeft | |
//Space at bottom left | |
case UpperRight | |
///Space at top right | |
case LowerLeft | |
///Space at top left | |
case LowerRight | |
var offsets : [Position] { | |
switch self { | |
case .UpperLeft: | |
return [Position(x: 0, y: 0), Position(x: -1, y: -1), Position(x: -1, y: 0)] | |
case .UpperRight: | |
return [Position(x: 0, y: 0), Position(x: 0, y: -1), Position(x: -1, y: 0)] | |
case .LowerLeft: | |
return [Position(x: 0, y: -1), Position(x: -1, y: 0), Position(x: -1, y: -1)] | |
case .LowerRight: | |
return [Position(x: 0, y: -1), Position(x: 0, y: 0), Position(x: -1, y: -1)] | |
} | |
} | |
static var allOrientations : [Orientation] = [.UpperLeft, .UpperRight, .LowerLeft, .LowerRight] | |
} | |
let orientation : Orientation | |
let position : Position | |
var description : String { | |
return "\(self.position.x) \(self.position.y) \(self.orientation)" | |
} | |
var hashValue: Int { | |
return (31 &* self.orientation.hashValue) &+ self.position.hashValue | |
} | |
} | |
func ==(lhs: Tromino, rhs: Tromino) -> Bool { | |
return lhs.orientation == rhs.orientation && lhs.position == rhs.position | |
} | |
enum SubBoard { | |
case UpperLeft | |
case UpperRight | |
case LowerLeft | |
case LowerRight | |
static var allSubBoards : [SubBoard] = [.UpperLeft, .UpperRight, .LowerLeft, .LowerRight] | |
} | |
protocol TrominoBoard { | |
var size : Int { get } | |
var centre : Position { get } | |
func placeTromino(atPosition position: Position, orientation: Tromino.Orientation) | |
subscript(position: Position) -> Tromino? { get } | |
func viewForSubBoard(_ subBoard: SubBoard) -> BoardView | |
mutating func tile(missingSquareLocation: Position) | |
} | |
final class Board : TrominoBoard, CustomStringConvertible { | |
let size : Int | |
private var boardArray : [Tromino?] | |
var centre: Position { | |
return Position(x: self.size / 2, y: self.size / 2) | |
} | |
init(size: Int) { | |
self.size = size | |
self.boardArray = [Tromino?](repeating: nil, count: size * size) | |
} | |
subscript(position: Position) -> Tromino? { | |
get { | |
return self.boardArray[position.y * self.size + position.x] | |
} | |
set (tromino) { | |
self.boardArray[position.y * self.size + position.x] = tromino | |
} | |
} | |
func placeTromino(atPosition position: Position, orientation: Tromino.Orientation) { | |
let tromino = Tromino(orientation: orientation, position: position) | |
for offset in orientation.offsets { | |
self[position + offset] = tromino | |
} | |
} | |
var boardView : BoardView { | |
return BoardView(size: self.size, centreInBoard: Position(x: self.size / 2, y: self.size / 2), board: self) | |
} | |
func viewForSubBoard(_ subBoard: SubBoard) -> BoardView { | |
return self.boardView.viewForSubBoard(subBoard) | |
} | |
var description : String { | |
let trominoes = Set<Tromino>(self.boardArray.flatMap { $0 }) | |
var output = "" | |
for tromino in trominoes { | |
output += "\(tromino)\n" | |
} | |
return output | |
} | |
var asciiArtView : String { | |
let trominoes = Set<Tromino>(self.boardArray.flatMap { $0 }) | |
var trominoesToCharacters = [Tromino : Character]() | |
let icons = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" | |
if trominoes.count > icons.count { | |
return "There are too many trominoes used in tiling this board to display." | |
} | |
for (i, tromino) in trominoes.enumerated() { | |
trominoesToCharacters[tromino] = icons.dropFirst(i).first! | |
} | |
var asciiArtView = "" | |
for y in (0..<self.size).reversed() { | |
for x in 0..<self.size { | |
if let tromino = self.boardArray[y * self.size + x] { | |
asciiArtView += String(trominoesToCharacters[tromino]!) | |
} else { | |
asciiArtView += " " | |
} | |
} | |
asciiArtView += "\n" | |
} | |
return asciiArtView | |
} | |
} | |
final class BoardView : TrominoBoard { | |
let size : Int | |
let centre : Position | |
private let board : Board | |
fileprivate init(size: Int, centreInBoard: Position, board: Board) { | |
self.size = size | |
self.centre = centreInBoard | |
self.board = board | |
} | |
func placeTromino(atPosition position: Position, orientation: Tromino.Orientation) { | |
let origin = self.centre - Position(x: self.size/2, y: self.size/2) | |
self.board.placeTromino(atPosition: position + origin, orientation: orientation) | |
} | |
subscript(position: Position) -> Tromino? { | |
return self.board[position + self.centre] | |
} | |
func viewForSubBoard(_ subBoard: SubBoard) -> BoardView { | |
let centreOffset = self.size / 4 | |
let centreInBoard : Position | |
switch subBoard { | |
case .UpperLeft: | |
centreInBoard = self.centre + Position(x: -centreOffset, y: centreOffset) | |
case .LowerLeft: | |
centreInBoard = self.centre + Position(x: -centreOffset, y: -centreOffset) | |
case .UpperRight: | |
centreInBoard = self.centre + Position(x: centreOffset, y: centreOffset) | |
case .LowerRight: | |
centreInBoard = self.centre + Position(x: centreOffset, y: -centreOffset) | |
} | |
return BoardView(size: self.size / 2, centreInBoard: centreInBoard, board: self.board) | |
} | |
} | |
extension TrominoBoard { | |
func subBoardContainingPosition(position: Position) -> SubBoard { | |
switch (position.x >= self.centre.x, position.y >= self.centre.y) { | |
case (true, true): | |
return .UpperRight | |
case (true, false): | |
return .LowerRight | |
case (false, true): | |
return .UpperLeft | |
case (false, false): | |
return .LowerLeft | |
} | |
} | |
mutating func tile(missingSquareLocation: Position) { | |
print("Placing a tile at \(missingSquareLocation); size is \(self.size)") | |
if self.size == 2 { | |
for orientation in Tromino.Orientation.allOrientations { | |
if !orientation.offsets.contains(where: { $0 + self.centre == missingSquareLocation }) { | |
self.placeTromino(atPosition: Position(x: 1, y: 1), orientation: orientation) | |
break | |
} | |
} | |
return | |
} | |
var emptySquaresForSubBoards = [SubBoard : Position]() | |
print("Empty squares: \(emptySquaresForSubBoards)") | |
let subBoardContainingMissingSquare = self.subBoardContainingPosition(position: missingSquareLocation) | |
print("subBoardContainingMissingSquare: \(subBoardContainingMissingSquare)") | |
emptySquaresForSubBoards[subBoardContainingMissingSquare] = missingSquareLocation | |
let centre = self.centre | |
//Place a tromino at the centre | |
do { | |
for orientation in Tromino.Orientation.allOrientations { | |
if !(orientation.offsets | |
.contains { | |
self.subBoardContainingPosition(position: centre + $0) == subBoardContainingMissingSquare | |
}) { | |
//This is the correct orientation if none of the tiles overlap the subboard with the missing tile. | |
self.placeTromino(atPosition: Position(x: self.size/2, y: self.size/2), orientation: orientation) | |
break | |
} | |
} | |
} | |
[Position(x: 0, y: 0), Position(x: -1, y: 0), Position(x: 0, y: -1), Position(x: -1, y: -1)].forEach { position in | |
let subBoard = self.subBoardContainingPosition(position: centre + position) | |
if subBoard != subBoardContainingMissingSquare { | |
emptySquaresForSubBoards[subBoard] = centre + position | |
} | |
} | |
for (subBoard, view) in SubBoard.allSubBoards.map({ ($0, self.viewForSubBoard($0)) }) { | |
var view = view | |
view.tile(missingSquareLocation: emptySquaresForSubBoards[subBoard]!) | |
} | |
} | |
} | |
let boardSize = 8 | |
let missingSquareLocation = Position(x: 3, y: 4) //position is 0-indexed from the bottom left. | |
var board = Board(size: boardSize) | |
board.tile(missingSquareLocation: missingSquareLocation) | |
print(board.description) | |
print("\n" + board.asciiArtView) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment