twisty_puzzles 0.0.1 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -1
  3. data/README.md +6 -1
  4. data/ext/twisty_puzzles/native/cube_algorithm.c +267 -0
  5. data/ext/twisty_puzzles/native/cube_algorithm.h +5 -0
  6. data/ext/twisty_puzzles/native/cube_average.c +184 -0
  7. data/ext/twisty_puzzles/native/cube_average.h +5 -0
  8. data/ext/twisty_puzzles/native/cube_coordinate.c +207 -0
  9. data/ext/twisty_puzzles/native/cube_coordinate.h +34 -0
  10. data/ext/twisty_puzzles/native/cube_state.c +264 -0
  11. data/ext/twisty_puzzles/native/cube_state.h +31 -0
  12. data/ext/twisty_puzzles/native/extconf.rb +1 -1
  13. data/ext/twisty_puzzles/native/face_symbols.c +67 -0
  14. data/ext/twisty_puzzles/native/face_symbols.h +34 -0
  15. data/ext/twisty_puzzles/native/native.c +28 -0
  16. data/ext/twisty_puzzles/native/skewb_algorithm.c +331 -0
  17. data/ext/twisty_puzzles/native/skewb_algorithm.h +5 -0
  18. data/ext/twisty_puzzles/native/skewb_coordinate.c +237 -0
  19. data/ext/twisty_puzzles/native/skewb_coordinate.h +36 -0
  20. data/ext/twisty_puzzles/native/skewb_layer_fingerprint.c +271 -0
  21. data/ext/twisty_puzzles/native/skewb_layer_fingerprint.h +5 -0
  22. data/ext/twisty_puzzles/native/skewb_state.c +214 -0
  23. data/ext/twisty_puzzles/native/skewb_state.h +23 -0
  24. data/ext/twisty_puzzles/native/utils.c +76 -0
  25. data/ext/twisty_puzzles/native/utils.h +31 -0
  26. data/lib/twisty_puzzles.rb +38 -0
  27. data/lib/twisty_puzzles/abstract_direction.rb +38 -39
  28. data/lib/twisty_puzzles/abstract_move.rb +1 -2
  29. data/lib/twisty_puzzles/abstract_move_parser.rb +32 -33
  30. data/lib/twisty_puzzles/algorithm.rb +112 -113
  31. data/lib/twisty_puzzles/algorithm_transformation.rb +19 -21
  32. data/lib/twisty_puzzles/axis_face_and_direction_move.rb +56 -56
  33. data/lib/twisty_puzzles/cancellation_helper.rb +124 -125
  34. data/lib/twisty_puzzles/color_scheme.rb +1 -1
  35. data/lib/twisty_puzzles/commutator.rb +82 -80
  36. data/lib/twisty_puzzles/compiled_algorithm.rb +31 -32
  37. data/lib/twisty_puzzles/compiled_cube_algorithm.rb +49 -50
  38. data/lib/twisty_puzzles/compiled_skewb_algorithm.rb +18 -19
  39. data/lib/twisty_puzzles/coordinate.rb +243 -246
  40. data/lib/twisty_puzzles/cube.rb +494 -495
  41. data/lib/twisty_puzzles/cube_constants.rb +40 -41
  42. data/lib/twisty_puzzles/cube_direction.rb +15 -18
  43. data/lib/twisty_puzzles/cube_move.rb +285 -290
  44. data/lib/twisty_puzzles/cube_move_parser.rb +75 -76
  45. data/lib/twisty_puzzles/cube_print_helper.rb +133 -133
  46. data/lib/twisty_puzzles/cube_state.rb +80 -81
  47. data/lib/twisty_puzzles/move_type_creator.rb +17 -18
  48. data/lib/twisty_puzzles/parser.rb +176 -179
  49. data/lib/twisty_puzzles/part_cycle_factory.rb +39 -42
  50. data/lib/twisty_puzzles/puzzle.rb +16 -17
  51. data/lib/twisty_puzzles/reversible_applyable.rb +24 -25
  52. data/lib/twisty_puzzles/rotation.rb +76 -75
  53. data/lib/twisty_puzzles/skewb_direction.rb +14 -15
  54. data/lib/twisty_puzzles/skewb_move.rb +49 -49
  55. data/lib/twisty_puzzles/skewb_move_parser.rb +51 -51
  56. data/lib/twisty_puzzles/skewb_notation.rb +121 -118
  57. data/lib/twisty_puzzles/skewb_state.rb +120 -121
  58. data/lib/twisty_puzzles/state_helper.rb +20 -21
  59. data/lib/twisty_puzzles/sticker_cycle.rb +43 -44
  60. data/lib/twisty_puzzles/utils.rb +3 -0
  61. data/lib/twisty_puzzles/version.rb +3 -1
  62. metadata +30 -10
@@ -0,0 +1,5 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ void init_skewb_layer_fingerprint_method_under(VALUE module);
@@ -0,0 +1,214 @@
1
+ #include "skewb_state.h"
2
+
3
+ #include "face_symbols.h"
4
+
5
+ static VALUE SkewbStateClass = Qnil;
6
+
7
+ static void SkewbStateData_mark(void* const ptr) {
8
+ const SkewbStateData* data = ptr;
9
+ for (size_t i = 0; i < total_skewb_stickers; ++i) {
10
+ rb_gc_mark(data->stickers[i]);
11
+ }
12
+ }
13
+
14
+ static size_t SkewbStateData_size(const void* const ptr) {
15
+ return sizeof(SkewbStateData);
16
+ }
17
+
18
+ const rb_data_type_t SkewbStateData_type = {
19
+ "SkewbTrainer::Native::SkewbStateData",
20
+ {SkewbStateData_mark, NULL, SkewbStateData_size, NULL},
21
+ NULL, NULL,
22
+ RUBY_TYPED_FREE_IMMEDIATELY
23
+ };
24
+
25
+ static VALUE SkewbState_alloc(const VALUE klass) {
26
+ SkewbStateData* data;
27
+ const VALUE object = TypedData_Make_Struct(klass, SkewbStateData, &SkewbStateData_type, data);
28
+ for (size_t i = 0; i < total_skewb_stickers; ++i) {
29
+ data->stickers[i] = Qnil;
30
+ }
31
+ return object;
32
+ }
33
+
34
+ static int SkewbState_replace_face(const VALUE key, const VALUE value, const VALUE self) {
35
+ SkewbStateData* data;
36
+ GetSkewbStateData(self, data);
37
+ const face_index_t on_face_index = face_index(key);
38
+ for (size_t i = 0; i < skewb_stickers_per_face; ++i) {
39
+ data->stickers[on_face_index * skewb_stickers_per_face + i] = value;
40
+ }
41
+ return ST_CONTINUE;
42
+ }
43
+
44
+ static VALUE SkewbState_initialize(const VALUE self, const VALUE stickers) {
45
+ Check_Type(stickers, T_HASH);
46
+ SkewbStateData* data;
47
+ GetSkewbStateData(self, data);
48
+ if (RHASH_SIZE(stickers) != skewb_faces) {
49
+ rb_raise(rb_eTypeError, "Skewbs must have %d faces. Got %ld.", skewb_faces, RHASH_SIZE(stickers));
50
+ }
51
+ rb_hash_foreach(stickers, SkewbState_replace_face, self);
52
+ return self;
53
+ }
54
+
55
+ static VALUE SkewbState_hash(const VALUE self) {
56
+ const SkewbStateData* data;
57
+ GetSkewbStateData(self, data);
58
+
59
+ st_index_t hash = rb_hash_start((st_index_t)SkewbState_hash);
60
+ for (size_t i = 0; i < total_skewb_stickers; i++) {
61
+ const VALUE sub_hash = rb_hash(data->stickers[i]);
62
+ hash = rb_hash_uint(hash, NUM2LONG(sub_hash));
63
+ }
64
+ return ST2FIX(rb_hash_end(hash));
65
+ }
66
+
67
+ static VALUE SkewbState_eql(const VALUE self, const VALUE other) {
68
+ if (self == other) {
69
+ return Qtrue;
70
+ }
71
+ if (rb_obj_class(self) != rb_obj_class(other)) {
72
+ return Qfalse;
73
+ }
74
+ const SkewbStateData* self_data;
75
+ GetSkewbStateData(self, self_data);
76
+ const SkewbStateData* other_data;
77
+ GetSkewbStateData(other, other_data);
78
+ for (size_t i = 0; i < total_skewb_stickers; ++i) {
79
+ if (!color_eq(self_data->stickers[i], other_data->stickers[i])) {
80
+ return Qfalse;
81
+ }
82
+ }
83
+ return Qtrue;
84
+ }
85
+
86
+ static VALUE SkewbState_dup(const VALUE self) {
87
+ const SkewbStateData* data;
88
+ GetSkewbStateData(self, data);
89
+ SkewbStateData* dupped_data;
90
+ const VALUE dupped = TypedData_Make_Struct(rb_obj_class(self), SkewbStateData, &SkewbStateData_type, dupped_data);
91
+ *dupped_data = *data;
92
+ return dupped;
93
+ }
94
+
95
+ static VALUE SkewbState_entry(const VALUE self, const VALUE coordinate) {
96
+ const SkewbStateData* data;
97
+ GetSkewbStateData(self, data);
98
+ return data->stickers[SkewbCoordinate_sticker_index(coordinate)];
99
+ }
100
+
101
+ static VALUE SkewbState_store(const VALUE self, const VALUE coordinate, const VALUE value) {
102
+ SkewbStateData* data;
103
+ GetSkewbStateData(self, data);
104
+ return data->stickers[SkewbCoordinate_sticker_index(coordinate)] = value;
105
+ }
106
+
107
+ static void apply_twisted_corner_cycle(VALUE* const stickers, const Corner corner, const bool invert) {
108
+ size_t twisted_corner_cycle[3];
109
+ for (size_t i = 0; i < 3; ++i) {
110
+ const Corner corner_variant = rotated_corner(corner, i);
111
+ twisted_corner_cycle[i] = corner_sticker_index(corner_variant);
112
+ }
113
+ apply_sticker_cycle(stickers, twisted_corner_cycle, 3, invert);
114
+ }
115
+
116
+ static void apply_center_cycle(VALUE* const stickers, const Corner corner, const bool invert) {
117
+ size_t center_cycle[3];
118
+ for (size_t i = 0; i < 3; ++i) {
119
+ center_cycle[i] = center_sticker_index(corner.face_indices[i]);
120
+ }
121
+ apply_sticker_cycle(stickers, center_cycle, 3, invert);
122
+ }
123
+
124
+ static void apply_moved_corner_cycles(VALUE* const stickers, const Corner corner, const bool invert) {
125
+ Corner adjacent_corners[3];
126
+ for (size_t corner_index = 0; corner_index < 3; ++corner_index) {
127
+ adjacent_corners[corner_index].face_indices[0] = opposite_face_index(corner.face_indices[corner_index]);
128
+ adjacent_corners[corner_index].face_indices[1] = corner.face_indices[(corner_index + 2) % 3];
129
+ adjacent_corners[corner_index].face_indices[2] = corner.face_indices[(corner_index + 1) % 3];
130
+ }
131
+ // We have 3 different cycles, one for the outside stickers and one two for the remaining stickers of the
132
+ // corners that contain the outside stickers.
133
+ for (size_t cycle_index = 0; cycle_index < 3; ++cycle_index) {
134
+ size_t sticker_cycle[3];
135
+ for (size_t corner_index = 0; corner_index < 3; ++corner_index) {
136
+ // The current corner, but twisted such that the sticker we care about in the current cycle faces up.
137
+ const Corner current_corner_for_current_cycle = rotated_corner(adjacent_corners[corner_index], cycle_index);
138
+ sticker_cycle[corner_index] = corner_sticker_index(current_corner_for_current_cycle);
139
+ }
140
+ apply_sticker_cycle(stickers, sticker_cycle, 3, invert);
141
+ }
142
+ }
143
+
144
+ void rotate_corner_for_skewb_state(const Corner corner, direction_t direction, SkewbStateData* const skewb_state) {
145
+ direction = CROP_MOD(direction, 3);
146
+ if (direction == 0) {
147
+ return;
148
+ }
149
+ const bool invert = direction == 2;
150
+
151
+ apply_twisted_corner_cycle(skewb_state->stickers, corner, invert);
152
+ apply_center_cycle(skewb_state->stickers, corner, invert);
153
+ apply_moved_corner_cycles(skewb_state->stickers, corner, invert);
154
+ }
155
+
156
+ static void apply_center_rotation(VALUE* const stickers, const face_index_t axis_face_index, const direction_t direction) {
157
+ Sticker4Cycle center_cycle;
158
+ for (size_t i = 0; i < 4; ++i) {
159
+ center_cycle.indices[i] = center_sticker_index(neighbor_face_index(axis_face_index, i));
160
+ }
161
+ apply_sticker_4cycle(stickers, center_cycle, direction);
162
+ }
163
+
164
+ static void apply_corner_rotations(VALUE* const stickers, const face_index_t axis_face_index, const direction_t direction) {
165
+ Corner corner;
166
+ corner.face_indices[2] = axis_face_index;
167
+ for (size_t cycle_index = 0; cycle_index < 3; ++cycle_index) {
168
+ Sticker4Cycle corner_cycle;
169
+ for (size_t corner_index = 0; corner_index < 4; ++corner_index) {
170
+ for (size_t i = 0; i < 2; ++i) {
171
+ corner.face_indices[i] = neighbor_face_index(axis_face_index, corner_index + i);
172
+ }
173
+ corner_cycle.indices[corner_index] = corner_sticker_index(rotated_corner(corner, cycle_index));
174
+ }
175
+ apply_sticker_4cycle(stickers, corner_cycle, direction);
176
+ }
177
+ }
178
+
179
+ void rotate_skewb_state(const face_index_t axis_face_index, direction_t direction, SkewbStateData* const skewb_state) {
180
+ direction = CROP_MOD(direction, 4);
181
+ if (direction == 0) {
182
+ return;
183
+ }
184
+ apply_center_rotation(skewb_state->stickers, axis_face_index, direction);
185
+ apply_corner_rotations(skewb_state->stickers, axis_face_index, direction);
186
+ apply_corner_rotations(skewb_state->stickers, opposite_face_index(axis_face_index), invert_cube_direction(direction));
187
+ }
188
+
189
+ static VALUE SkewbState_face_solved(const VALUE self, const VALUE face_symbol) {
190
+ const SkewbStateData* data;
191
+ GetSkewbStateData(self, data);
192
+ const face_index_t solved_face_index = face_index(face_symbol);
193
+ const size_t center_index = center_sticker_index(solved_face_index);
194
+ const VALUE color = data->stickers[center_index];
195
+ for (int i = 1; i < 5; ++i) {
196
+ if (!color_eq(data->stickers[center_index + i], color)) {
197
+ return Qfalse;
198
+ }
199
+ }
200
+ return Qtrue;
201
+ }
202
+
203
+ void init_skewb_state_class_under(const VALUE module) {
204
+ SkewbStateClass = rb_define_class_under(module, "SkewbState", rb_cObject);
205
+ rb_define_alloc_func(SkewbStateClass, SkewbState_alloc);
206
+ rb_define_method(SkewbStateClass, "initialize", SkewbState_initialize, 1);
207
+ rb_define_method(SkewbStateClass, "hash", SkewbState_hash, 0);
208
+ rb_define_method(SkewbStateClass, "eql?", SkewbState_eql, 1);
209
+ rb_define_alias(SkewbStateClass, "==", "eql?");
210
+ rb_define_method(SkewbStateClass, "dup", SkewbState_dup, 0);
211
+ rb_define_method(SkewbStateClass, "[]", SkewbState_entry, 1);
212
+ rb_define_method(SkewbStateClass, "[]=", SkewbState_store, 2);
213
+ rb_define_method(SkewbStateClass, "face_solved?", SkewbState_face_solved, 1);
214
+ }
@@ -0,0 +1,23 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ #include "skewb_coordinate.h"
6
+ #include "utils.h"
7
+
8
+ typedef struct {
9
+ VALUE stickers[total_skewb_stickers];
10
+ } SkewbStateData;
11
+
12
+ extern const rb_data_type_t SkewbStateData_type;
13
+
14
+ #define GetSkewbStateData(obj, data) \
15
+ do { \
16
+ TypedData_Get_Struct((obj), SkewbStateData, &SkewbStateData_type, (data)); \
17
+ } while (0)
18
+
19
+ void rotate_corner_for_skewb_state(Corner corner, direction_t direction, SkewbStateData* skewb_state);
20
+
21
+ void rotate_skewb_state(face_index_t axis_face_index, direction_t direction, SkewbStateData* skewb_state);
22
+
23
+ void init_skewb_state_class_under(VALUE module);
@@ -0,0 +1,76 @@
1
+ #include "utils.h"
2
+
3
+ static void swap(VALUE* const stickers, size_t i, size_t j) {
4
+ VALUE buffer;
5
+ buffer = stickers[i];
6
+ stickers[i] = stickers[j];
7
+ stickers[j] = buffer;
8
+ }
9
+
10
+ direction_t invert_cube_direction(direction_t direction) {
11
+ return (4 - direction) % 4;
12
+ }
13
+
14
+ direction_t invert_skewb_direction(direction_t direction) {
15
+ return (3 - direction) % 3;
16
+ }
17
+
18
+ void apply_sticker_cycle(VALUE* const stickers, const size_t* const indices, const size_t size, const bool invert) {
19
+ const size_t direction = invert ? size - 1 : 1;
20
+ const size_t last_index = (direction * (size - 1)) % size;
21
+ const VALUE buffer = stickers[indices[last_index]];
22
+ for (size_t i = size - 1; i >= 1; --i) {
23
+ const size_t target_index = (direction * i) % size;
24
+ const size_t source_index = (direction * (i - 1)) % size;
25
+ stickers[indices[target_index]] = stickers[indices[source_index]];
26
+ }
27
+ stickers[indices[0]] = buffer;
28
+ }
29
+
30
+ void apply_sticker_4cycle(VALUE* const stickers, const Sticker4Cycle cycle, const direction_t direction) {
31
+ const direction_t d = (direction % 4 + 4) % 4;
32
+ if (d == 0) {
33
+ return;
34
+ } else if (d == 2) {
35
+ swap(stickers, cycle.indices[0], cycle.indices[2]);
36
+ swap(stickers, cycle.indices[1], cycle.indices[3]);
37
+ } else {
38
+ apply_sticker_cycle(stickers, cycle.indices, 4, d == 3);
39
+ }
40
+ }
41
+
42
+ int color_eq(const VALUE left, const VALUE right) {
43
+ return RTEST(rb_equal(left, right));
44
+ }
45
+
46
+ // Magic table of values that we use for computing the log2.
47
+ static const int tab64[64] = {
48
+ 63, 0, 58, 1, 59, 47, 53, 2,
49
+ 60, 39, 48, 27, 54, 33, 42, 3,
50
+ 61, 51, 37, 40, 49, 18, 28, 20,
51
+ 55, 30, 34, 11, 43, 14, 22, 4,
52
+ 62, 57, 46, 52, 38, 26, 32, 41,
53
+ 50, 36, 17, 19, 29, 10, 13, 21,
54
+ 56, 45, 25, 31, 35, 16, 9, 12,
55
+ 44, 24, 15, 8, 23, 7, 6, 5};
56
+
57
+ int log2_64_floor(uint64_t value) {
58
+ value |= value >> 1;
59
+ value |= value >> 2;
60
+ value |= value >> 4;
61
+ value |= value >> 8;
62
+ value |= value >> 16;
63
+ value |= value >> 32;
64
+ return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58];
65
+ }
66
+
67
+ uint64_t iexp(const uint64_t base, uint32_t exp) {
68
+ uint64_t result = 1;
69
+ for (uint32_t m = 1 << 31; m; m >>= 1) {
70
+ result = result * result;
71
+ if (m & exp) {
72
+ result *= base;
73
+ }
74
+ }
75
+ return result;
76
+ }
@@ -0,0 +1,31 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+ #include <stdint.h>
5
+
6
+ #define MIN(a, b) ((a) > (b) ? (b) : (a))
7
+ #define MAX(a, b) ((a) > (b) ? (a) : (b))
8
+ #define FALSE 0
9
+ #define TRUE 1
10
+ #define CROP_MOD(a, b) (((a) % (b) + (b)) % (b))
11
+
12
+ typedef char bool;
13
+ typedef char direction_t;
14
+
15
+ typedef struct {
16
+ size_t indices[4];
17
+ } Sticker4Cycle;
18
+
19
+ direction_t invert_cube_direction(direction_t direction);
20
+
21
+ direction_t invert_skewb_direction(direction_t direction);
22
+
23
+ void apply_sticker_cycle(VALUE* stickers, const size_t* indices, size_t size, bool invert);
24
+
25
+ void apply_sticker_4cycle(VALUE* stickers, Sticker4Cycle cycle, direction_t direction);
26
+
27
+ int color_eq(VALUE left, VALUE right);
28
+
29
+ int log2_64_floor(uint64_t value);
30
+
31
+ uint64_t iexp(uint64_t base, uint32_t exp);
@@ -1,5 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'twisty_puzzles/abstract_direction'
4
+ require 'twisty_puzzles/abstract_move'
5
+ require 'twisty_puzzles/abstract_move_parser'
6
+ require 'twisty_puzzles/algorithm'
7
+ require 'twisty_puzzles/algorithm_transformation'
8
+ require 'twisty_puzzles/axis_face_and_direction_move'
9
+ require 'twisty_puzzles/cancellation_helper'
10
+ require 'twisty_puzzles/color_scheme'
11
+ require 'twisty_puzzles/commutator'
12
+ require 'twisty_puzzles/compiled_algorithm'
13
+ require 'twisty_puzzles/compiled_cube_algorithm'
14
+ require 'twisty_puzzles/compiled_skewb_algorithm'
15
+ require 'twisty_puzzles/coordinate'
16
+ require 'twisty_puzzles/cube'
17
+ require 'twisty_puzzles/cube_constants'
18
+ require 'twisty_puzzles/cube_direction'
19
+ require 'twisty_puzzles/cube_move'
20
+ require 'twisty_puzzles/cube_move_parser'
21
+ require 'twisty_puzzles/cube_print_helper'
22
+ require 'twisty_puzzles/cube_state'
23
+ require 'twisty_puzzles/letter_scheme'
24
+ require 'twisty_puzzles/move_type_creator'
25
+ require 'twisty_puzzles/native'
26
+ require 'twisty_puzzles/parser'
27
+ require 'twisty_puzzles/part_cycle_factory'
28
+ require 'twisty_puzzles/puzzle'
29
+ require 'twisty_puzzles/reversible_applyable'
30
+ require 'twisty_puzzles/rotation'
31
+ require 'twisty_puzzles/skewb_direction'
32
+ require 'twisty_puzzles/skewb_move'
33
+ require 'twisty_puzzles/skewb_move_parser'
34
+ require 'twisty_puzzles/skewb_notation'
35
+ require 'twisty_puzzles/skewb_state'
36
+ require 'twisty_puzzles/state_helper'
37
+ require 'twisty_puzzles/sticker_cycle'
38
+ require 'twisty_puzzles/twisty_puzzles_error'
39
+ require 'twisty_puzzles/version'
40
+
3
41
  # Libraries for the twisty puzzles.
4
42
  module TwistyPuzzles
5
43
  end
@@ -1,54 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TwistyPuzzles
4
-
5
- # Base class for directions.
6
- class AbstractDirection
7
- include Comparable
8
- POSSIBLE_DIRECTION_NAMES = [[''], ['2', '2\''], ['\'', '3']].freeze
9
- SIMPLE_DIRECTION_NAMES = (['0'] + POSSIBLE_DIRECTION_NAMES.map(&:first)).freeze
10
- POSSIBLE_SKEWB_DIRECTION_NAMES = [['', '2\''], ['\'', '2']].freeze
11
- SIMPLE_SKEWB_DIRECTION_NAMES = (['0'] + POSSIBLE_SKEWB_DIRECTION_NAMES.map(&:first)).freeze
12
-
13
- def initialize(value)
14
- raise TypeError, "Direction value #{value} isn't an integer." unless value.is_a?(Integer)
15
- unless value >= 0 && value < self.class::NUM_DIRECTIONS
16
- raise ArgumentError, "Invalid direction value #{value}."
17
- end
18
-
19
- @value = value
4
+ # Base class for directions.
5
+ class AbstractDirection
6
+ include Comparable
7
+ POSSIBLE_DIRECTION_NAMES = [[''], ['2', '2\''], ['\'', '3']].freeze
8
+ SIMPLE_DIRECTION_NAMES = (['0'] + POSSIBLE_DIRECTION_NAMES.map(&:first)).freeze
9
+ POSSIBLE_SKEWB_DIRECTION_NAMES = [['', '2\''], ['\'', '2']].freeze
10
+ SIMPLE_SKEWB_DIRECTION_NAMES = (['0'] + POSSIBLE_SKEWB_DIRECTION_NAMES.map(&:first)).freeze
11
+
12
+ def initialize(value)
13
+ raise TypeError, "Direction value #{value} isn't an integer." unless value.is_a?(Integer)
14
+ unless value >= 0 && value < self.class::NUM_DIRECTIONS
15
+ raise ArgumentError, "Invalid direction value #{value}."
20
16
  end
21
17
 
22
- attr_reader :value
18
+ @value = value
19
+ end
23
20
 
24
- def <=>(other)
25
- @value <=> other.value
26
- end
21
+ attr_reader :value
27
22
 
28
- def zero?
29
- @value.zero?
30
- end
23
+ def <=>(other)
24
+ @value <=> other.value
25
+ end
31
26
 
32
- def non_zero?
33
- @value.positive?
34
- end
27
+ def zero?
28
+ @value.zero?
29
+ end
35
30
 
36
- def inverse
37
- self.class.new((self.class::NUM_DIRECTIONS - @value) % self.class::NUM_DIRECTIONS)
38
- end
31
+ def non_zero?
32
+ @value.positive?
33
+ end
39
34
 
40
- def +(other)
41
- self.class.new((@value + other.value) % self.class::NUM_DIRECTIONS)
42
- end
35
+ def inverse
36
+ self.class.new((self.class::NUM_DIRECTIONS - @value) % self.class::NUM_DIRECTIONS)
37
+ end
43
38
 
44
- def eql?(other)
45
- self.class.equal?(other.class) && @value == other.value
46
- end
39
+ def +(other)
40
+ self.class.new((@value + other.value) % self.class::NUM_DIRECTIONS)
41
+ end
42
+
43
+ def eql?(other)
44
+ self.class.equal?(other.class) && @value == other.value
45
+ end
47
46
 
48
- alias == eql?
47
+ alias == eql?
49
48
 
50
- def hash
51
- @value.hash
52
- end
49
+ def hash
50
+ @value.hash
53
51
  end
52
+ end
54
53
  end