├── .gitignore ├── CoreRubiksCube.playground ├── Contents.swift ├── Sources │ └── CoreRubiksCube.swift ├── contents.xcplayground └── playground.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | *~.nib 4 | build/ 5 | */build/* 6 | *.pbxuser 7 | *.perspective 8 | *.perspectivev3 9 | *.mode1v3 10 | *.mode2v3 11 | xcuserdata/ 12 | DerivedData 13 | *.xccheckout 14 | *.lock 15 | *.moved-aside 16 | .Trashes 17 | ._* 18 | -------------------------------------------------------------------------------- /CoreRubiksCube.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Cocoa 4 | 5 | 6 | let cube = Cube() 7 | 8 | print(cube) 9 | 10 | assert(cube.makeFront(.counterclockwise).makeFront(.clockwise) == cube) 11 | assert(cube.makeBack(.counterclockwise).makeBack(.clockwise) == cube) 12 | assert(cube.makeUp(.counterclockwise).makeUp(.clockwise) == cube) 13 | assert(cube.makeDown(.counterclockwise).makeDown(.clockwise) == cube) 14 | assert(cube.makeLeft(.counterclockwise).makeLeft(.clockwise) == cube) 15 | assert(cube.makeRight(.counterclockwise).makeRight(.clockwise) == cube) 16 | 17 | assert(cube.makeFront(.double).makeFront(.double) == cube) 18 | assert(cube.makeBack(.double).makeBack(.double) == cube) 19 | assert(cube.makeUp(.double).makeUp(.double) == cube) 20 | assert(cube.makeDown(.double).makeDown(.double) == cube) 21 | assert(cube.makeLeft(.double).makeLeft(.double) == cube) 22 | assert(cube.makeRight(.double).makeRight(.double) == cube) 23 | 24 | assert(cube.makeMove(Movement.front(.clockwise)).makeMove(Movement.front(.counterclockwise)) == cube) 25 | assert(cube.makeMove(Movement.back(.clockwise)).makeMove(Movement.back(.counterclockwise)) == cube) 26 | assert(cube.makeMove(Movement.up(.clockwise)).makeMove(Movement.up(.counterclockwise)) == cube) 27 | assert(cube.makeMove(Movement.down(.clockwise)).makeMove(Movement.down(.counterclockwise)) == cube) 28 | assert(cube.makeMove(Movement.left(.clockwise)).makeMove(Movement.left(.counterclockwise)) == cube) 29 | assert(cube.makeMove(Movement.right(.clockwise)).makeMove(Movement.right(.counterclockwise)) == cube) 30 | 31 | assert(cube.makeMove(Movement.front(.double)).makeMove(Movement.front(.double)) == cube) 32 | assert(cube.makeMove(Movement.back(.double)).makeMove(Movement.back(.double)) == cube) 33 | assert(cube.makeMove(Movement.up(.double)).makeMove(Movement.up(.double)) == cube) 34 | assert(cube.makeMove(Movement.down(.double)).makeMove(Movement.down(.double)) == cube) 35 | assert(cube.makeMove(Movement.left(.double)).makeMove(Movement.left(.double)) == cube) 36 | assert(cube.makeMove(Movement.right(.double)).makeMove(Movement.right(.double)) == cube) 37 | 38 | 39 | let moves: [Movement] = [ 40 | .front(.clockwise), .back(.clockwise), 41 | .up(.clockwise), .down(.clockwise), 42 | .left(.clockwise), .right(.clockwise), 43 | .front(.counterclockwise), .back(.counterclockwise), 44 | .up(.counterclockwise), .down(.counterclockwise), 45 | .left(.counterclockwise), .right(.counterclockwise), 46 | .front(.double), .back(.double), 47 | .up(.double), .down(.double), 48 | .left(.double), .right(.double) 49 | ] 50 | 51 | assert(cube.makeMoves(moves).reverseMoves(moves) == cube) 52 | 53 | -------------------------------------------------------------------------------- /CoreRubiksCube.playground/Sources/CoreRubiksCube.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreRubiksCube.swift 3 | // CoreRubiksCube 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2016 Electricwoods LLC, Kaz Yoshikawa. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | 30 | // Overview 31 | // -------- 32 | // This code is for implementing Rubik's Cube's model and it's behavior. It is 33 | // not intended for providing any graphical interface or solver related code. 34 | // It is however, provides basic functionality to turn faces, to reverse 35 | // turns, and to examine the state of the cube. 36 | // 37 | // Xcode 9.2, Swift 4 38 | 39 | // Face: 40 | // A Rubik's Cube has six faces. In order to identify specific face, each face has 41 | // its own unique name. We assume, face will not move around, instead any faces 42 | // can be turned or spined, and each surfaces stay the same place. 43 | // 44 | 45 | public enum Face { 46 | case front 47 | case back 48 | case up 49 | case down 50 | case left 51 | case right 52 | } 53 | 54 | 55 | // Tile: 56 | // Tile represent a single piece of tile of a surface. Each surface has 57 | // 3x3 (9) tiles. In order to identify each tiles of the cube, each tile 58 | // has own unique identifier as follows. The naming idea id taking from 59 | // this site http://iamthecu.be. 60 | // 61 | // 62 | // |U18|U19|U20| : 63 | // |U10|U11|U12| : +-----+ 64 | // |U01|U02|U03| : | U | 65 | // |L18|L10|L01|F01|F02|F03|R03|R12|R20|B20|B19|B18| : +-----+-----+-----+-----+ 66 | // |L21|L13|L04|F04|F05|F06|R06|R14|R23|B23|B22|B21| : | L | F | R | B | 67 | // |L24|L15|L07|F07|F08|F09|R09|R17|R16|B26|B25|B24| : +-----+-----+-----+-----+ 68 | // |D07|D08|D09| : | D | 69 | // |D15|D16|D17| : +-----+ 70 | // |D24|D25|D26| : 71 | // ** Figure 1 ** 72 | 73 | public enum Tile { 74 | case F01, F02, F03, F06, F09, F08, F07, F04, F05 // front 75 | case U18, U19, U20, U12, U03, U02, U01, U10, U11 // up 76 | case R03, R12, R20, R23, R26, R17, R09, R06, R14 // right 77 | case B24, B25, B26, B23, B20, B19, B18, B21, B22 // back 78 | case D07, D08, D09, D17, D26, D25, D24, D15, D16 // down 79 | case L18, L10, L01, L04, L07, L15, L24, L21, L13 // left 80 | } 81 | 82 | // Turn: 83 | // Each surface can be turned in either `clockwise`, `counterclockwise` or `double` spins. 84 | 85 | public enum Turn { 86 | case clockwise 87 | case counterclockwise 88 | case `double` 89 | 90 | public func reversed() -> Turn { 91 | switch self { 92 | case .clockwise: return .counterclockwise 93 | case .counterclockwise: return .clockwise 94 | case .double: return .double 95 | } 96 | } 97 | } 98 | 99 | // Movement: 100 | // A cube can be turn any faces, `Movement` specifies which face to make turn. 101 | // It has an associated value with it. 102 | 103 | public enum Movement { 104 | case front(Turn) 105 | case back(Turn) 106 | case up(Turn) 107 | case down(Turn) 108 | case left(Turn) 109 | case right(Turn) 110 | } 111 | 112 | // Surface: 113 | // A `Surface` specifies all tiles on a face. In order to specify a tile on a face, 114 | // we assign `a` to `i` for each positions of tiles. All positions apply to all faces 115 | // in figure 1 including `back` face which can be controversial. 116 | // 117 | // +---+---+---+ 118 | // | a | b | c | 119 | // +---+---+---+ 120 | // | d | e | f | 121 | // +---+---+---+ 122 | // | g | h | i | 123 | // +---+---+---+ 124 | // ** Figure 2 ** 125 | 126 | public struct Surface: CustomStringConvertible, Equatable { 127 | public let (a, b, c): (Tile, Tile, Tile) 128 | public let (d, e, f): (Tile, Tile, Tile) 129 | public let (g, h, i): (Tile, Tile, Tile) 130 | 131 | init(_ a: Tile, _ b: Tile, _ c: Tile, _ d: Tile, _ e: Tile, _ f: Tile, _ g: Tile, _ h: Tile, _ i: Tile) { 132 | (self.a, self.b, self.c) = (a, b, c) 133 | (self.d, self.e, self.f) = (d, e, f) 134 | (self.g, self.h, self.i) = (g, h, i) 135 | } 136 | 137 | init(face: Face) { 138 | switch (face) { 139 | case .front: (a, b, c, d, e, f, g, h, i) = (.F01, .F02, .F03, .F04, .F05, .F06, .F07, .F08, .F09) 140 | case .back: (a, b, c, d, e, f, g, h, i) = (.B20, .B19, .B18, .B23, .B22, .B21, .B26, .B25, .B24) 141 | case .up: (a, b, c, d, e, f, g, h, i) = (.U18, .U19, .U20, .U10, .U11, .U12, .U01, .U02, .U03) 142 | case .down: (a, b, c, d, e, f, g, h, i) = (.D07, .D08, .D09, .D15, .D16, .D17, .D24, .D25, .D26) 143 | case .left: (a, b, c, d, e, f, g, h, i) = (.L18, .L10, .L01, .L21, .L13, .L04, .L24, .L15, .L07) 144 | case .right: (a, b, c, d, e, f, g, h, i) = (.R03, .R12, .R20, .R06, .R14, .R23, .R09, .R17, .R26) 145 | } 146 | } 147 | 148 | public var description: String { 149 | return [[a, b, c], [d, e, f], [g, h, i]].map { $0.map { String(describing: $0) }.joined(separator: "|") }.joined(separator: "\n") 150 | } 151 | 152 | func turn(_ turn: Turn) -> Surface { 153 | switch turn { 154 | case .clockwise: return Surface(g, d, a, h, e, b, i, f, c) 155 | case .counterclockwise: return Surface(c, f, i, b, e, h, a, d, g) 156 | case .double: return Surface(i, h, g, f, e, d, c, b, a) 157 | } 158 | } 159 | 160 | // accessors to pick three tiles in a row 161 | var abc: (Tile, Tile, Tile) { return (a, b, c) } 162 | var cba: (Tile, Tile, Tile) { return (c, b, a) } 163 | var ghi: (Tile, Tile, Tile) { return (g, h, i) } 164 | var ihg: (Tile, Tile, Tile) { return (i, h, g) } 165 | var adg: (Tile, Tile, Tile) { return (a, d, g) } 166 | var gda: (Tile, Tile, Tile) { return (g, d, a) } 167 | var cfi: (Tile, Tile, Tile) { return (c, f, i) } 168 | var ifc: (Tile, Tile, Tile) { return (i, f, c) } 169 | 170 | // returns a surface replaced by a row with other row 171 | func replaced(abc: (a: Tile, b: Tile, c: Tile)) -> Surface { 172 | return Surface(abc.a, abc.b, abc.c, d, e, f, g, h, i) 173 | } 174 | func replaced(ghi: (g: Tile, h: Tile, i: Tile)) -> Surface { 175 | return Surface(a, b, c, d, e, f, ghi.g, ghi.h, ghi.i) 176 | } 177 | func replaced(adg: (a: Tile, d: Tile, g: Tile)) -> Surface { 178 | return Surface(adg.a, b, c, adg.d, e, f, adg.g, h, i) 179 | } 180 | func replaced(cfi: (c: Tile, f: Tile, i: Tile)) -> Surface { 181 | return Surface(a, b, cfi.c, d, e, cfi.f, g, h, cfi.i) 182 | } 183 | 184 | // to conform Equatable 185 | public static func == (lhs: Surface, rhs: Surface) -> Bool { 186 | return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c && 187 | lhs.d == rhs.d && lhs.e == rhs.e && lhs.f == rhs.f && 188 | lhs.g == rhs.g && lhs.h == rhs.h && lhs.i == rhs.i 189 | } 190 | } 191 | 192 | // Cube: 193 | // `Cube` represnet a complete tiles of all faces 194 | // 195 | 196 | public struct Cube: CustomStringConvertible, Equatable { 197 | public let front: Surface 198 | public let back: Surface 199 | public let up: Surface 200 | public let down: Surface 201 | public let left: Surface 202 | public let right: Surface 203 | 204 | 205 | public init(front: Surface, back: Surface, up: Surface, down: Surface, left: Surface, right: Surface) { 206 | self.front = front 207 | self.back = back 208 | self.up = up 209 | self.down = down 210 | self.left = left 211 | self.right = right 212 | } 213 | 214 | public init() { 215 | front = Surface(face: .front) 216 | back = Surface(face: .back) 217 | up = Surface(face: .up) 218 | down = Surface(face: .down) 219 | left = Surface(face: .left) 220 | right = Surface(face: .right) 221 | } 222 | 223 | // accessor to retrieve a specific surface for the face 224 | public func surface(face: Face) -> Surface { 225 | switch (face) { 226 | case .front: return front 227 | case .back: return back 228 | case .up: return up 229 | case .down: return down 230 | case .left: return left 231 | case .right: return right 232 | } 233 | } 234 | 235 | // mostly debuging purpose 236 | public var description: String { 237 | let (f, b, u, d, l, r) = (front, back, up, down, left, right) 238 | return ([ 239 | [ nil, nil, nil, u.a, u.b, u.c, nil, nil, nil, nil, nil, nil ], 240 | [ nil, nil, nil, u.d, u.e, u.f, nil, nil, nil, nil, nil, nil ], 241 | [ nil, nil, nil, u.g, u.h, u.i, nil, nil, nil, nil, nil, nil ], 242 | [ l.a, l.b, l.c, f.a, f.b, f.c, r.a, r.b, r.c, b.a, b.b, b.c ], 243 | [ l.d, l.e, l.f, f.d, f.e, f.f, r.d, r.e, r.f, b.d, b.e, b.f ], 244 | [ l.g, l.h, l.i, f.g, f.h, f.i, r.g, r.h, r.i, b.g, b.h, b.i ], 245 | [ nil, nil, nil, d.a, d.b, d.c, nil, nil, nil, nil, nil, nil ], 246 | [ nil, nil, nil, d.d, d.e, d.f, nil, nil, nil, nil, nil, nil ], 247 | [ nil, nil, nil, d.g, d.h, d.i, nil, nil, nil, nil, nil, nil ] 248 | ] as [[Tile?]]) 249 | .map { "|" + $0.map { $0 != nil ? String(describing: $0!) + "|" : " |" }.joined() }.joined(separator: "\n") 250 | } 251 | 252 | // make a turn on fornt face 253 | public func makeFront(_ turn: Turn) -> Cube { 254 | let f = front.turn(turn) 255 | switch turn { 256 | case .clockwise: 257 | let u = up.replaced(ghi: left.ifc) 258 | let r = right.replaced(adg: up.ghi) 259 | let d = down.replaced(abc: right.gda) 260 | let l = left.replaced(cfi: down.abc) 261 | return Cube(front: f, back: back, up: u, down: d, left: l, right: r) 262 | case .counterclockwise: 263 | let u = up.replaced(ghi: right.adg) 264 | let r = right.replaced(adg: down.cba) 265 | let d = down.replaced(abc: left.cfi) 266 | let l = left.replaced(cfi: up.ihg) 267 | return Cube(front: f, back: back, up: u, down: d, left: l, right: r) 268 | case .double: 269 | let u = up.replaced(ghi: down.cba) 270 | let r = right.replaced(adg: left.ifc) 271 | let d = down.replaced(abc: up.ihg) 272 | let l = left.replaced(cfi: right.gda) 273 | return Cube(front: f, back: back, up: u, down: d, left: l, right: r) 274 | } 275 | } 276 | 277 | // make a turn on back face 278 | public func makeBack(_ turn: Turn) -> Cube { 279 | let b = back.turn(turn) 280 | switch turn { 281 | case .clockwise: 282 | let u = up.replaced(abc: right.cfi) 283 | let r = right.replaced(cfi: down.ihg) 284 | let d = down.replaced(ghi: left.adg) 285 | let l = left.replaced(adg: up.cba) 286 | return Cube(front: front, back: b, up: u, down: d, left: l, right: r) 287 | case .counterclockwise: 288 | let u = up.replaced(abc: left.gda) 289 | let r = right.replaced(cfi: up.abc) 290 | let d = down.replaced(ghi: right.ifc) 291 | let l = left.replaced(adg: down.ghi) 292 | return Cube(front: front, back: b, up: u, down: d, left: l, right: r) 293 | case .double: 294 | let u = up.replaced(abc: down.ihg) 295 | let r = right.replaced(cfi: left.gda) 296 | let d = down.replaced(ghi: up.cba) 297 | let l = left.replaced(adg: right.ifc) 298 | return Cube(front: front, back: b, up: u, down: d, left: l, right: r) 299 | } 300 | } 301 | 302 | // make a turn on up face 303 | public func makeUp(_ turn: Turn) -> Cube { 304 | let u = up.turn(turn) 305 | switch turn { 306 | case .clockwise: 307 | let f = front.replaced(abc: right.abc) 308 | let l = left.replaced(abc: front.abc) 309 | let b = back.replaced(abc: left.abc) 310 | let r = right.replaced(abc: back.abc) 311 | return Cube(front: f, back: b, up: u, down: down, left: l, right: r) 312 | case .counterclockwise: 313 | let f = front.replaced(abc: left.abc) 314 | let l = left.replaced(abc: back.abc) 315 | let b = back.replaced(abc: right.abc) 316 | let r = right.replaced(abc: front.abc) 317 | return Cube(front: f, back: b, up: u, down: down, left: l, right: r) 318 | case .double: 319 | let f = front.replaced(abc: back.abc) 320 | let l = left.replaced(abc: right.abc) 321 | let b = back.replaced(abc: front.abc) 322 | let r = right.replaced(abc: left.abc) 323 | return Cube(front: f, back: b, up: u, down: down, left: l, right: r) 324 | } 325 | } 326 | 327 | // make a turn on down face 328 | public func makeDown(_ turn: Turn) -> Cube { 329 | let d = down.turn(turn) 330 | switch turn { 331 | case .clockwise: 332 | let f = front.replaced(ghi: left.ghi) 333 | let l = left.replaced(ghi: back.ghi) 334 | let b = back.replaced(ghi: right.ghi) 335 | let r = right.replaced(ghi: front.ghi) 336 | return Cube(front: f, back: b, up: up, down: d, left: l, right: r) 337 | case .counterclockwise: 338 | let f = front.replaced(ghi: right.ghi) 339 | let l = left.replaced(ghi: front.ghi) 340 | let b = back.replaced(ghi: left.ghi) 341 | let r = right.replaced(ghi: back.ghi) 342 | return Cube(front: f, back: b, up: up, down: d, left: l, right: r) 343 | case .double: 344 | let f = front.replaced(ghi: back.ghi) 345 | let l = left.replaced(ghi: right.ghi) 346 | let b = back.replaced(ghi: front.ghi) 347 | let r = right.replaced(ghi: left.ghi) 348 | return Cube(front: f, back: b, up: up, down: d, left: l, right: r) 349 | } 350 | } 351 | 352 | // make a turn on right face 353 | public func makeRight(_ turn: Turn) -> Cube { 354 | let r = right.turn(turn) 355 | switch turn { 356 | case .clockwise: 357 | let f = front.replaced(cfi: down.cfi) 358 | let u = up.replaced(cfi: front.cfi) 359 | let b = back.replaced(adg: up.ifc) 360 | let d = down.replaced(cfi: back.gda) 361 | return Cube(front: f, back: b, up: u, down: d, left: left, right: r) 362 | case .counterclockwise: 363 | let f = front.replaced(cfi: up.cfi) 364 | let u = up.replaced(cfi: back.gda) 365 | let b = back.replaced(adg: down.ifc) 366 | let d = down.replaced(cfi: front.cfi) 367 | return Cube(front: f, back: b, up: u, down: d, left: left, right: r) 368 | case .double: 369 | let f = front.replaced(cfi: back.gda) 370 | let u = up.replaced(cfi: down.cfi) 371 | let b = back.replaced(adg: front.ifc) 372 | let d = down.replaced(cfi: up.cfi) 373 | return Cube(front: f, back: b, up: u, down: d, left: left, right: r) 374 | } 375 | } 376 | 377 | // make a turn on left face 378 | public func makeLeft(_ turn: Turn) -> Cube { 379 | let l = left.turn(turn) 380 | switch turn { 381 | case .clockwise: 382 | let f = front.replaced(adg: up.adg) 383 | let u = up.replaced(adg: back.ifc) 384 | let b = back.replaced(cfi: down.gda) 385 | let d = down.replaced(adg: front.adg) 386 | return Cube(front: f, back: b, up: u, down: d, left: l, right: right) 387 | case .counterclockwise: 388 | let f = front.replaced(adg: down.adg) 389 | let u = up.replaced(adg: front.adg) 390 | let b = back.replaced(cfi: up.gda) 391 | let d = down.replaced(adg: back.ifc) 392 | return Cube(front: f, back: b, up: u, down: d, left: l, right: right) 393 | case .double: 394 | let f = front.replaced(adg: back.ifc) 395 | let u = up.replaced(adg: down.adg) 396 | let b = back.replaced(cfi: front.gda) 397 | let d = down.replaced(adg: up.adg) 398 | return Cube(front: f, back: b, up: u, down: d, left: l, right: right) 399 | } 400 | } 401 | 402 | // make a single turn 403 | public func makeMove(_ movement: Movement) -> Cube { 404 | switch movement { 405 | case .front(let turn): return makeFront(turn) 406 | case .back(let turn): return makeBack(turn) 407 | case .up(let turn): return makeUp(turn) 408 | case .down(let turn): return makeDown(turn) 409 | case .left(let turn): return makeLeft(turn) 410 | case .right(let turn): return makeRight(turn) 411 | } 412 | } 413 | 414 | // reverse a single turn 415 | public func reverseMove(_ movement: Movement) -> Cube { 416 | switch movement { 417 | case .front(let turn): return makeFront(turn.reversed()) 418 | case .back(let turn): return makeBack(turn.reversed()) 419 | case .up(let turn): return makeUp(turn.reversed()) 420 | case .down(let turn): return makeDown(turn.reversed()) 421 | case .left(let turn): return makeLeft(turn.reversed()) 422 | case .right(let turn): return makeRight(turn.reversed()) 423 | } 424 | } 425 | 426 | // make a sequence of turns 427 | public func makeMoves(_ movements: [Movement]) -> Cube { 428 | var cube = self 429 | for movement in movements { 430 | cube = cube.makeMove(movement) 431 | } 432 | return cube 433 | } 434 | 435 | // reverse a sequence of turns 436 | public func reverseMoves(_ movements: [Movement]) -> Cube { 437 | var cube = self 438 | for movement in movements.reversed() { 439 | cube = cube.reverseMove(movement) 440 | } 441 | return cube 442 | } 443 | 444 | // to conform Equatable 445 | public static func == (lhs: Cube, rhs: Cube) -> Bool { 446 | return lhs.front == rhs.front && lhs.back == rhs.back && 447 | lhs.up == rhs.up && lhs.down == rhs.down && 448 | lhs.left == rhs.left && lhs.right == rhs.right 449 | } 450 | } 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | -------------------------------------------------------------------------------- /CoreRubiksCube.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /CoreRubiksCube.playground/playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CoreRubiksCube.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Core Rubik's Cube 2 | 3 | 4 | ![xcode](https://img.shields.io/badge/Xcode-11.4-blue) 5 | ![swift](https://img.shields.io/badge/Swift-5.2-orange.svg) 6 | ![license](https://img.shields.io/badge/License-MIT-yellow.svg) 7 | 8 | 9 | 10 | Core Rublik's Cube is a piece of code to implement basic implementation of Rubik's Cube. It does not offer any graphical user interface nor feature to solve the cube. It does, however, provide turn faces or to reverse those turns to find the state of the cube programmatically. 11 | 12 | ### Usage 13 | 14 | You may instantiate a cube with initialized positions, and print it as follows. 15 | 16 | ```.swift 17 | let cube1 = Cube() 18 | print(cube1) 19 | ``` 20 | 21 | The cube's state should be printed to console as follows. As you can see, each tile has it's own unique identifier on it. 22 | 23 | ``` 24 | | | | |U18|U19|U20| | | | | | | 25 | | | | |U10|U11|U12| | | | | | | 26 | | | | |U01|U02|U03| | | | | | | 27 | |L18|L10|L01|F01|F02|F03|R03|R12|R20|B20|B19|B18| 28 | |L21|L13|L04|F04|F05|F06|R06|R14|R23|B23|B22|B21| 29 | |L24|L15|L07|F07|F08|F09|R09|R17|R26|B26|B25|B24| 30 | | | | |D07|D08|D09| | | | | | | 31 | | | | |D15|D16|D17| | | | | | | 32 | | | | |D24|D25|D26| | | | | | | 33 | ``` 34 | 35 | For getting some ideas about faces on a cube, here is the image of the unfolded faces of a cube. F, B, U, D, L and R are stands for front, back, up, down, left and right. They are commonly used for Rubik's Cube community. 36 | 37 | ``` 38 | +---+ 39 | | U | 40 | +---+---+---+---+ 41 | | L | F | R | B | 42 | +---+---+---+---+ 43 | | D | 44 | +---+ 45 | ``` 46 | 47 | Then you may turn front face counter clockwise and print as follows. 48 | 49 | ```.swift 50 | let cube2 = cube1.makeFront(.counterclockwise) 51 | print(cube2) 52 | ``` 53 | You will find front face and it's related cubelets are rotated counter clockwise. 54 | 55 | 56 | ``` 57 | | | | |U18|U19|U20| | | | | | | 58 | | | | |U10|U11|U12| | | | | | | 59 | | | | |R03|R06|R09| | | | | | | 60 | |L18|L10|U03|F03|F06|F09|D09|R12|R20|B20|B19|B18| 61 | |L21|L13|U02|F02|F05|F08|D08|R14|R23|B23|B22|B21| 62 | |L24|L15|U01|F01|F04|F07|D07|R17|R26|B26|B25|B24| 63 | | | | |L01|L04|L07| | | | | | | 64 | | | | |D15|D16|D17| | | | | | | 65 | | | | |D24|D25|D26| | | | | | | 66 | ``` 67 | 68 | You may examine any surface by accessing `front`, `back`, `up`, `down`, `left` and `right` properties. Here is the code to print `front` surface of the cube. 69 | 70 | ```.swift 71 | print(cube.front) 72 | ``` 73 | You can find 9 identifiers from a surface, and each identifier represent an unique tile of a cube. 74 | 75 | ``` 76 | F01|F02|F03 77 | F04|F05|F06 78 | F07|F08|F09 79 | ``` 80 | 81 | All tiles on a cube are defined as enum as follows. 82 | 83 | ```.swift 84 | public enum Tile { 85 | case F01, F02, F03, F06, F09, F08, F07, F04, F05 // front 86 | case U18, U19, U20, U12, U03, U02, U01, U10, U11 // up 87 | case R03, R12, R20, R23, R26, R17, R09, R06, R14 // right 88 | case B24, B25, B26, B23, B20, B19, B18, B21, B22 // back 89 | case D07, D08, D09, D17, D26, D25, D24, D15, D16 // down 90 | case L18, L10, L01, L04, L07, L15, L24, L21, L13 // left 91 | } 92 | ``` 93 | 94 | Tile positions on a surface are described as follows. 95 | 96 | ``` 97 | +---+---+---+ 98 | | a | b | c | 99 | +---+---+---+ 100 | | d | e | f | 101 | +---+---+---+ 102 | | g | h | i | 103 | +---+---+---+ 104 | ``` 105 | 106 | So, you can access to any position of a surface by accessing `a`, `a`, `b`, `c`, `d`, `e`, `f`, `g`, `h` and `i` properties. 107 | 108 | ```.swift 109 | cube.front.g // F07 110 | cube.up.b // U19 111 | cube.back.a // B20 112 | ``` 113 | 114 | You may make a turn of any surface of a cube. `Cube` provides some method to make a single rotation of any face of a cube. Those methods do not make any changes of the instance itself, rather they return a new instance with made rotation given by parameter `Turn`. 115 | 116 | ```.swift 117 | public func makeFront(_ turn: Turn) -> Cube 118 | public func makeBack(_ turn: Turn) -> Cube 119 | public func makeUp(_ turn: Turn) -> Cube 120 | public func makeDown(_ turn: Turn) -> Cube 121 | public func makeleft(_ turn: Turn) -> Cube 122 | public func makeRight(_ turn: Turn) -> Cube 123 | ``` 124 | 125 | Turn describe which direction to make a turn either `clockwise`, `counterclockwise` or `double`. `Double` turns a surface twice in the same direction, therefore, regardless clockwise or counter clockwise, the results are the same. 126 | 127 | ```.swift 128 | public enum Turn { 129 | case clockwise 130 | case counterclockwise 131 | case `double` 132 | } 133 | ``` 134 | 135 | You may provide a sequence of `Movement` which describes face and turn direction to perform multiple turns in once just like following example. 136 | 137 | 138 | ```.swift 139 | let moves: [Movement] = 140 | [ 141 | .front(turn: .clockwise), 142 | .back(turn: .clockwise), 143 | .left(turn: .counterclockwise), 144 | .right(turn: .counterclockwise), 145 | .up(turn: .double), 146 | .down(turn: .double) 147 | ] 148 | let cube3 = cube.makeMoves(moves) 149 | ``` 150 | 151 | You may perform the same sequence backward to reverse the operation. 152 | 153 | ```.swift 154 | let cube4 = cube3.reverseMoves(moves) 155 | ``` 156 | 157 | Since, `Cube` conforms to `Equatable` protocol, you may make sure reversing operation actually work. 158 | 159 | ```.swift 160 | cube4 == cube // true 161 | ``` 162 | 163 | ### Types 164 | 165 | | Type | Description | 166 | |:--------------|:------------| 167 | | Face | Specifies a face direction of cube | 168 | | Surface | Specifies a face of cube that contains tiles | 169 | | Tile | A suface of a cubelet. A surface contains 3x3, 9 `Tile`s. | 170 | | Turn | A turn direction either `clockwise`, `counterclockwise`, or `double` | 171 | | Movement | A single turn action which face and which turn direction | 172 | | Cube | A whole cube with six surfaces | 173 | 174 | 175 | ### Why did I do this? 176 | 177 | Good question, but I do not have a good answer for that question. 178 | 179 | 180 | ### Environment 181 | 182 | ``` 183 | Version 11.4.1 (11E503a) 184 | Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51) 185 | Target: x86_64-apple-darwin19.4.0 186 | ``` 187 | 188 | --------------------------------------------------------------------------------