twisty_puzzles 0.0.1 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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