1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or is a part of
11 # Rubix-cube modelization library
13 # As for now the library supports basic representation of a Rubix-cube
14 # The library is built for speed, most operations cost no allocations to perform.
15 # This does however mean that the library is NOT thread-safe and should be handled
16 # with the appropriate mutual-exclusion mechanisms to avoid memory corruption
17 # or unintended side-effects on a single cube.
19 # The library supports the singmaster notation as a way to perform operations
22 # No solver is (yet) provided along with the library.
27 private fun array1d_copy_to
(fromarr
: Array[Int], oarr
: Array[Int]) do
28 while oarr
.length
> fromarr
.length
do oarr
.pop
29 while oarr
.length
< fromarr
.length
do oarr
.push
0
30 fromarr
.copy_to
(0, fromarr
.length
, oarr
, 0)
33 private fun front_face
: Int do return 0
34 private fun left_face
: Int do return 1
35 private fun top_face
: Int do return 2
36 private fun right_face
: Int do return 3
37 private fun bottom_face
: Int do return 4
38 private fun back_face
: Int do return 5
40 private fun top_ln
: Int do return 0
41 private fun mid_ln
: Int do return 1
42 private fun bottom_ln
: Int do return 2
44 private fun left_col
: Int do return 0
45 private fun mid_col
: Int do return 1
46 private fun right_col
: Int do return 2
48 private fun clock
: Int do return 0
49 private fun counterclock
: Int do return 1
51 private fun square
: String do return once
"■"
55 # Returns a coloured square for a defined colour id
60 # * White (replaced with light gray) -> 1
63 # * Orange (replaced with purple) -> 4
66 private fun rubix_colour
: String do
67 if self == 0 then return square
.green
68 if self == 1 then return square
.light_gray
69 if self == 2 then return square
.red
70 if self == 3 then return square
.yellow
71 if self == 4 then return square
.purple
72 if self == 5 then return square
.blue
77 # In-memory representation of a Rubix Cube
79 # Faces are represented in memory as if they were a flattened cube like:
96 # All the commands detailed in the class respect the Singmaster notation
112 var faces
: Array[Array[Array[Int]]]
114 # Build a new Rubix Cube with a solved layout
116 var faces
= new Array[Array[Array[Int]]]
119 var face
= new Array[Array[Int]]
122 var line
= new Array[Int]
133 # Build a new Rubix Cube with a scrambled layout
135 # NOTE: The layout is not random, but scrambled nonetheless
137 var colours
= once
[0, 1, 2, 3, 4, 5]
139 var faces
= new Array[Array[Array[Int]]]
142 var face
= new Array[Array[Int]]
145 var line
= new Array[Int]
147 line
.add colours
[colour_pos
]
148 colour_pos
+= increment
149 if colour_pos
> 5 then
153 if colour_pos
< 0 then
164 # Reset the Rubix Cube to a solved position
177 # Checks if both objects are Rubix cubes and their content is equivalent
179 # NOTE: Rotationed versions are not yet considered equal
181 if not o
isa RubixCube then return false
182 for mf
in faces
, tf
in o
.faces
do
183 for ml
in mf
, tl
in tf
do
184 for mc
in ml
, tc
in tl
do if mc
!= tc
then return false
190 # Is `self` a solved Rubix Cube ?
191 fun is_solved
: Bool do
192 for face_id
in [0 .. 6[ do
193 var i
= faces
[face_id
]
194 var colour
= i
.first
.first
198 if ln
[k
] != colour
then return false
207 buf
.append
(single_face
(back_face
))
208 buf
.append
(single_face
(top_face
))
209 buf
.append
(three_faces
(left_face
, front_face
, right_face
))
210 buf
.append
(single_face
(bottom_face
))
214 private fun single_face
(face_id
: Int): Text do
216 var face
= faces
[face_id
]
219 b
.append
("{" " * 6}{ln[0].rubix_colour} {ln[1].rubix_colour} {ln[2].rubix_colour}{" " * 6}\n")
224 private fun three_faces
(face1
, face2
, face3
: Int): Text do
226 var face_ids
= [face1
, face2
, face3
]
231 b
.append
("{ln[0].rubix_colour} {ln[1].rubix_colour} {ln[2].rubix_colour} ")
238 private var rot_ln_buffer
= new Array[Array[Int]].with_capacity
(4)
239 private fun rotate_line
(ln_id
: Int, direction
: Int) do
240 var line_data
= rot_ln_buffer
241 if line_data
.is_empty
then for i
in [0 .. 4[ do line_data
.add
(new Array[Int])
242 array1d_copy_to
(faces
[front_face
][ln_id
], line_data
[0])
243 array1d_copy_to
(faces
[left_face
][ln_id
], line_data
[1])
244 array1d_copy_to
(faces
[back_face
][2 - ln_id
], line_data
[2])
245 array1d_copy_to
(faces
[right_face
][ln_id
], line_data
[3])
246 if direction
== counterclock
then
247 line_data
[3].swap_at
(0, 2)
248 line_data
[2].swap_at
(0, 2)
249 rot_ln_buffer
.rotate_left
250 else if direction
== clock
then
251 line_data
[1].swap_at
(0, 2)
252 line_data
[2].swap_at
(0, 2)
253 rot_ln_buffer
.rotate_right
257 array1d_copy_to
(line_data
[0], faces
[front_face
][ln_id
])
258 array1d_copy_to
(line_data
[1], faces
[left_face
][ln_id
])
259 array1d_copy_to
(line_data
[2], faces
[back_face
][2 - ln_id
])
260 array1d_copy_to
(line_data
[3], faces
[right_face
][ln_id
])
263 private var colbuf
= new Array[Int].with_capacity
(3)
264 private fun coldata
(face_id
: Int, col_id
: Int): Array[Int] do
265 if colbuf
.is_empty
then for i
in [0 .. 3[ do colbuf
.add
0
266 var face
= faces
[face_id
]
267 for i
in [0 .. 3[ do colbuf
[i
] = face
[i
][col_id
]
271 private fun set_coldata
(face_id
, col_id
: Int, coldata
: Array[Int]) do
272 var face
= faces
[face_id
]
273 for i
in [0 .. 3[ do face
[i
][col_id
] = coldata
[i
]
276 private var rot_col_buffer
= new Array[Array[Int]].with_capacity
(4)
277 private fun rotate_column
(col_id
: Int, direction
: Int) do
278 var col_data
= rot_col_buffer
279 if col_data
.is_empty
then for i
in [0 .. 4[ do col_data
.add
(new Array[Int])
280 array1d_copy_to
(coldata
(front_face
, col_id
), rot_col_buffer
[0])
281 array1d_copy_to
(coldata
(top_face
, col_id
), rot_col_buffer
[1])
282 array1d_copy_to
(coldata
(back_face
, col_id
), rot_col_buffer
[2])
283 array1d_copy_to
(coldata
(bottom_face
, col_id
), rot_col_buffer
[3])
284 if direction
== clock
then
285 rot_col_buffer
.rotate_left
286 else if direction
== counterclock
then
287 rot_col_buffer
.rotate_right
291 set_coldata
(front_face
, col_id
, rot_col_buffer
[0])
292 set_coldata
(top_face
, col_id
, rot_col_buffer
[1])
293 set_coldata
(back_face
, col_id
, rot_col_buffer
[2])
294 set_coldata
(bottom_face
, col_id
, rot_col_buffer
[3])
297 private var r90_cache
= new Array[Array[Int]]
298 private fun rotate_l90_face
(face_id
: Int) do
299 var lines
= r90_cache
300 if lines
.is_empty
then for i
in [0 .. 3[ do lines
.add
(new Array[Int])
301 array1d_copy_to
(faces
[face_id
][top_ln
], lines
[0])
302 array1d_copy_to
(faces
[face_id
][mid_ln
], lines
[1])
303 array1d_copy_to
(faces
[face_id
][bottom_ln
], lines
[2])
304 for i
in [0 .. 3[ do lines
[i
].swap_at
(0, 2)
305 set_coldata
(face_id
, left_col
, lines
[0])
306 set_coldata
(face_id
, mid_col
, lines
[1])
307 set_coldata
(face_id
, right_col
, lines
[2])
310 private fun rotate_r90_face
(face_id
: Int) do
311 var lines
= r90_cache
312 if lines
.is_empty
then for i
in [0 .. 3[ do lines
.add
(new Array[Int])
313 array1d_copy_to
(faces
[face_id
][top_ln
], lines
[0])
314 array1d_copy_to
(faces
[face_id
][mid_ln
], lines
[1])
315 array1d_copy_to
(faces
[face_id
][bottom_ln
], lines
[2])
316 set_coldata
(face_id
, right_col
, lines
[0])
317 set_coldata
(face_id
, mid_col
, lines
[1])
318 set_coldata
(face_id
, left_col
, lines
[2])
323 rotate_line
(top_ln
, clock
)
324 rotate_r90_face
(top_face
)
329 rotate_line
(top_ln
, counterclock
)
330 rotate_l90_face
(top_face
)
335 rotate_line
(bottom_ln
, counterclock
)
336 rotate_r90_face
(bottom_face
)
341 rotate_line
(bottom_ln
, clock
)
342 rotate_l90_face
(bottom_face
)
347 rotate_column
(left_col
, clock
)
348 rotate_r90_face
(left_face
)
353 rotate_column
(left_col
, counterclock
)
354 rotate_l90_face
(left_face
)
359 rotate_column
(right_col
, counterclock
)
360 rotate_r90_face
(right_face
)
365 rotate_column
(right_col
, clock
)
366 rotate_l90_face
(right_face
)
370 fun clock_M
do rotate_column
(mid_col
, clock
)
373 fun cclock_M
do rotate_column
(mid_col
, counterclock
)
376 fun clock_E
do rotate_line
(mid_ln
, counterclock
)
379 fun cclock_E
do rotate_line
(mid_ln
, clock
)
496 fun cube_Y_rotation
do
503 fun ccube_Y_rotation
do
510 fun cube_X_rotation
do
517 fun ccube_X_rotation
do
524 fun cube_Z_rotation
do
531 fun ccube_Z_rotation
do
537 # Applies a command `cmd` to `self`, returns the number of operations performed during the command
538 fun do_cmd
(cmd
: String): Int do
539 if cmd
== "" then return 0
541 var cmdln
= cmd
.length
542 if cmd
[cmdln
- 1] == '2' then
549 if cmd2
== '2' then cmd2
= '\0'
551 for i
in [1 .. iters
] do
558 else if cmd1 == 'L
' then
564 else if cmd1
== 'F' then
570 else if cmd1 == 'R
' then
576 else if cmd1
== 'B' then
582 else if cmd1 == 'D
' then
588 else if cmd1
== 'M' then
594 else if cmd1 == 'E
' then
600 else if cmd1
== 'S' then
606 else if cmd1 == 'u
' then
612 else if cmd1
== 'l' then
618 else if cmd1 == 'f
' then
624 else if cmd1
== 'r' then
630 else if cmd1 == 'b
' then
636 else if cmd1
== 'd' then
642 else if cmd1 == 'X
' then
648 else if cmd1
== 'Y' then
654 else if cmd1 == 'Z
' then
668 redef fun force_console_colors
do return true