Skip to content

Instantly share code, notes, and snippets.

@troughton
Last active November 30, 2018 11:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save troughton/9fdecf4dc09b0406c444c28f40f80d43 to your computer and use it in GitHub Desktop.
Save troughton/9fdecf4dc09b0406c444c28f40f80d43 to your computer and use it in GitHub Desktop.
Swift Tromino Algorithm - Windows Test
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