twisty_puzzles 0.0.6 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 59a43662a260f2e013e0b59b6597dbfc4f6b5a04bc9d49b9f350b9c3a050babe
4
- data.tar.gz: 98e23ce0bbcb677b9deb490ca02bb2e6c3fbd376c0c5a35dcb79d6e06afbd905
3
+ metadata.gz: 0f6166de8734039f856869be9f20b6109c194556094bc281ae3138ada4a1ad27
4
+ data.tar.gz: 6a19cc1ad482ec49831c057d944b9055f70c206882ac618892b0336e8d047e3b
5
5
  SHA512:
6
- metadata.gz: fb187fa6e58c48444b5629e4ab195f2063d2ad9b0c8f07aa8d3a4e19486c177fb29b38a404e2a80f0690d6491466f94cd638d04fc6d76389ffb915671c2107a4
7
- data.tar.gz: 9b89321ce8b01b446b992a318aaf43f2cdc54bc9745d16538f14e2bc93f038a413923a11c0e856e13712e20a29c6d0416344ed577ea5aac642ef295571d489a3
6
+ metadata.gz: 3ba31f44cb0df6f836d9a9b9908550cd2d6ca0ede672d4784f084b609e1249fd0e8e0f8d6cdc0b09e8faba6a462d49966c698b9d5601a4901aa0ee4eb8bc95c1
7
+ data.tar.gz: 16727e63c37191c99ed1c185c851c8a541c97f110505f2c6ae5eeeabb186c11c35173d35c2441cc8c15318dd88f380656afc04690d5bb4eaa9b359b9a2725de3
@@ -18,12 +18,12 @@ typedef struct {
18
18
  CubeMoveType type;
19
19
  face_index_t axis_face_index;
20
20
  direction_t direction;
21
- size_t slice_index;
21
+ long slice_index;
22
22
  } CubeMove;
23
23
 
24
24
  typedef struct {
25
25
  size_t size;
26
- size_t cube_size;
26
+ long cube_size;
27
27
  CubeMove* moves;
28
28
  } CubeAlgorithmData;
29
29
 
@@ -45,15 +45,6 @@ const rb_data_type_t CubeAlgorithmData_type = {
45
45
  RUBY_TYPED_FREE_IMMEDIATELY
46
46
  };
47
47
 
48
- static void check_moves(const CubeAlgorithmData* const data, const char* const name) {
49
- for (size_t i = 0; i < data->size; ++i) {
50
- const CubeMoveType type = data->moves[i].type;
51
- if (type != SLICE && type != FACE) {
52
- rb_raise(rb_eRuntimeError, "invalid move type %d in %s", type, name);
53
- }
54
- }
55
- }
56
-
57
48
  static CubeMove* malloc_moves(const size_t n) {
58
49
  CubeMove* const moves = malloc(n * sizeof(CubeMove));
59
50
  if (moves == NULL) {
@@ -96,7 +87,7 @@ static CubeMoveType extract_move_type(const VALUE move_symbol) {
96
87
  }
97
88
  }
98
89
 
99
- static size_t components_for_move_type(const CubeMoveType type) {
90
+ static long components_for_move_type(const CubeMoveType type) {
100
91
  switch (type) {
101
92
  case SLICE:
102
93
  return 4;
@@ -112,24 +103,25 @@ static VALUE CubeAlgorithm_initialize(const VALUE self, const VALUE cube_size, c
112
103
  CubeAlgorithmData* data;
113
104
  GetCubeAlgorithmData(self, data);
114
105
  data->size = RARRAY_LEN(moves);
115
- data->cube_size = NUM2INT(cube_size);
106
+ data->cube_size = FIX2INT(cube_size);
107
+ check_cube_size(data->cube_size);
116
108
  data->moves = malloc_moves(data->size);
117
- for (size_t i = 0; i < RARRAY_LEN(moves); ++i) {
109
+ for (long i = 0; i < RARRAY_LEN(moves); ++i) {
118
110
  const VALUE move = rb_ary_entry(moves, i);
119
111
  if (RARRAY_LEN(move) < 1) {
120
112
  rb_raise(rb_eArgError, "Moves cannot be empty.");
121
113
  }
122
114
  const CubeMoveType type = extract_move_type(rb_ary_entry(move, 0));
123
- const size_t num_components = components_for_move_type(type);
115
+ const long num_components = components_for_move_type(type);
124
116
  if (RARRAY_LEN(move) != num_components) {
125
117
  rb_raise(rb_eArgError, "Moves with the given type need to have %ld elements. Got %ld.", num_components, RARRAY_LEN(move));
126
118
  }
127
119
  data->moves[i].type = type;
128
120
  data->moves[i].axis_face_index = face_index(rb_ary_entry(move, 1));
129
- data->moves[i].direction = NUM2INT(rb_ary_entry(move, 2));
121
+ data->moves[i].direction = FIX2INT(rb_ary_entry(move, 2));
130
122
  if (type == SLICE) {
131
- const size_t slice_index = NUM2INT(rb_ary_entry(move, 3));
132
- if (slice_index >= data->cube_size) {
123
+ const long slice_index = FIX2INT(rb_ary_entry(move, 3));
124
+ if (slice_index < 0 || slice_index >= data->cube_size) {
133
125
  rb_raise(rb_eArgError, "Invalid slice index %ld for cube size %ld.", slice_index, data->cube_size);
134
126
  }
135
127
  data->moves[i].slice_index = slice_index;
@@ -59,7 +59,7 @@ static VALUE CubeAverage_alloc(const VALUE klass) {
59
59
 
60
60
  static VALUE CubeAverage_initialize(const VALUE self, const VALUE capacity, const VALUE initial_average) {
61
61
  Check_Type(capacity, T_FIXNUM);
62
- const size_t n = FIX2INT(capacity);
62
+ const long n = FIX2INT(capacity);
63
63
  if (n < 3) {
64
64
  rb_raise(rb_eArgError, "The number of elements for a cube average has to be at least 3. Got %ld.", n);
65
65
  }
@@ -147,7 +147,7 @@ static VALUE CubeAverage_push_all(const VALUE self, const VALUE new_values) {
147
147
  const size_t insert_index = data->insert_index;
148
148
  const size_t capacity = data->capacity;
149
149
  const size_t start = num_values > capacity ? num_values - capacity : 0;
150
- for (size_t i = start; i < RARRAY_LEN(new_values); ++i) {
150
+ for (long i = start; i < RARRAY_LEN(new_values); ++i) {
151
151
  const VALUE new_value = rb_ary_entry(new_values, i);
152
152
  data->values[(insert_index + i) % capacity] = NUM2DBL(new_value);
153
153
  }
@@ -5,7 +5,7 @@
5
5
  static VALUE CubeCoordinateClass = Qnil;
6
6
 
7
7
  typedef struct {
8
- size_t cube_size;
8
+ long cube_size;
9
9
  face_index_t on_face_index;
10
10
  Point point;
11
11
  } CubeCoordinateData;
@@ -26,15 +26,15 @@ const rb_data_type_t CubeCoordinateData_type = {
26
26
  TypedData_Get_Struct((obj), CubeCoordinateData, &CubeCoordinateData_type, (data)); \
27
27
  } while (0)
28
28
 
29
- size_t num_stickers(const size_t cube_size) {
29
+ size_t num_stickers(const long cube_size) {
30
30
  return cube_faces * cube_size * cube_size;
31
31
  }
32
32
 
33
- size_t sticker_index(const size_t cube_size, const face_index_t on_face_index, const Point point) {
33
+ size_t sticker_index(const long cube_size, const face_index_t on_face_index, const Point point) {
34
34
  return on_face_index * cube_size * cube_size + point.y * cube_size + point.x;
35
35
  }
36
36
 
37
- size_t CubeCoordinate_sticker_index(const VALUE self, const size_t cube_size) {
37
+ size_t CubeCoordinate_sticker_index(const VALUE self, const long cube_size) {
38
38
  CubeCoordinateData* data;
39
39
  GetCubeCoordinateData(self, data);
40
40
  if (data->cube_size != cube_size) {
@@ -53,11 +53,11 @@ static VALUE CubeCoordinate_alloc(const VALUE klass) {
53
53
  return object;
54
54
  }
55
55
 
56
- static size_t inverted_index(const size_t cube_size, const size_t index) {
56
+ static long inverted_index(const long cube_size, const long index) {
57
57
  return cube_size - 1 - index;
58
58
  }
59
59
 
60
- size_t transform_index(const face_index_t index_base_face_index, const size_t cube_size, const size_t index) {
60
+ long transform_index(const face_index_t index_base_face_index, const long cube_size, const long index) {
61
61
  if (index_base_face_index == axis_index(index_base_face_index)) {
62
62
  return index;
63
63
  } else {
@@ -86,11 +86,11 @@ void check_base_face_indices(const face_index_t on_face_index,
86
86
  Point point_on_face(const face_index_t face_index,
87
87
  const face_index_t x_base_face_index,
88
88
  const face_index_t y_base_face_index,
89
- const size_t cube_size,
90
- const size_t untransformed_x,
91
- const size_t untransformed_y) {
92
- const size_t transformed_x = transform_index(x_base_face_index, cube_size, untransformed_x);
93
- const size_t transformed_y = transform_index(y_base_face_index, cube_size, untransformed_y);
89
+ const long cube_size,
90
+ const long untransformed_x,
91
+ const long untransformed_y) {
92
+ const long transformed_x = transform_index(x_base_face_index, cube_size, untransformed_x);
93
+ const long transformed_y = transform_index(y_base_face_index, cube_size, untransformed_y);
94
94
  Point point;
95
95
  if (switch_axes(x_base_face_index, y_base_face_index)) {
96
96
  point.x = transformed_y;
@@ -102,7 +102,7 @@ Point point_on_face(const face_index_t face_index,
102
102
  return point;
103
103
  }
104
104
 
105
- static void check_cube_index(const size_t cube_size, const size_t index) {
105
+ static void check_cube_index(const long cube_size, const long index) {
106
106
  if (index < 0 || index >= cube_size) {
107
107
  rb_raise(rb_eArgError, "Invalid value %ld for x with cube size %ld.", index, cube_size);
108
108
  }
@@ -121,14 +121,15 @@ static VALUE CubeCoordinate_initialize(const VALUE self,
121
121
  Check_Type(y_base_face_symbol, T_SYMBOL);
122
122
  Check_Type(x_num, T_FIXNUM);
123
123
  Check_Type(y_num, T_FIXNUM);
124
- const size_t n = FIX2INT(cube_size);
124
+ const long n = FIX2INT(cube_size);
125
+ check_cube_size(n);
125
126
  const face_index_t on_face_index = face_index(face_symbol);
126
127
  const face_index_t x_base_face_index = face_index(x_base_face_symbol);
127
128
  const face_index_t y_base_face_index = face_index(y_base_face_symbol);
128
129
  check_base_face_indices(on_face_index, x_base_face_index, y_base_face_index);
129
- const size_t untransformed_x = FIX2INT(x_num);
130
+ const long untransformed_x = FIX2INT(x_num);
130
131
  check_cube_index(n, untransformed_x);
131
- const size_t untransformed_y = FIX2INT(y_num);
132
+ const long untransformed_y = FIX2INT(y_num);
132
133
  check_cube_index(n, untransformed_y);
133
134
  const Point point = point_on_face(on_face_index, x_base_face_index, y_base_face_index, n, untransformed_x, untransformed_y);
134
135
  CubeCoordinateData* data;
@@ -5,16 +5,16 @@
5
5
  #include "face_symbols.h"
6
6
  #include "utils.h"
7
7
 
8
- size_t num_stickers(size_t cube_size);
8
+ size_t num_stickers(long cube_size);
9
9
 
10
10
  typedef struct {
11
11
  size_t x;
12
12
  size_t y;
13
13
  } Point;
14
14
 
15
- size_t sticker_index(size_t cube_size, face_index_t on_face_index, Point point);
15
+ size_t sticker_index(long cube_size, face_index_t on_face_index, Point point);
16
16
 
17
- size_t transform_index(face_index_t index_base_face_index, size_t cube_size, size_t index);
17
+ long transform_index(face_index_t index_base_face_index, long cube_size, long index);
18
18
 
19
19
  bool switch_axes(face_index_t x_base_face_index, face_index_t y_base_face_index);
20
20
 
@@ -25,10 +25,10 @@ void check_base_face_indices(face_index_t on_face_index,
25
25
  Point point_on_face(face_index_t on_face_index,
26
26
  face_index_t x_base_face_index,
27
27
  face_index_t y_base_face_index,
28
- size_t cube_size,
29
- size_t untransformed_x,
30
- size_t untransformed_y);
28
+ long cube_size,
29
+ long untransformed_x,
30
+ long untransformed_y);
31
31
 
32
- size_t CubeCoordinate_sticker_index(VALUE self, size_t cube_size);
32
+ size_t CubeCoordinate_sticker_index(VALUE self, long cube_size);
33
33
 
34
34
  void init_cube_coordinate_class_under(VALUE module);
@@ -12,7 +12,7 @@ static VALUE CubeStateClass = Qnil;
12
12
 
13
13
  static void CubeStateData_mark(void* const ptr) {
14
14
  const CubeStateData* data = ptr;
15
- const size_t n = data->cube_size;
15
+ const long n = data->cube_size;
16
16
  for (size_t i = 0; i < num_stickers(n); ++i) {
17
17
  rb_gc_mark(data->stickers[i]);
18
18
  }
@@ -52,19 +52,10 @@ static VALUE CubeState_alloc(const VALUE klass) {
52
52
  return object;
53
53
  }
54
54
 
55
- static size_t extract_index_base_face_index(const VALUE face_hash, const VALUE key) {
56
- const VALUE index_base_face_symbol = rb_hash_aref(face_hash, key);
57
- if (index_base_face_symbol == Qnil) {
58
- rb_raise(rb_eTypeError, "Cube faces must have keys called :{x,y}_base_face_symbol that describes which face an x or y value of 0 is close to.");
59
- }
60
- Check_Type(index_base_face_symbol, T_SYMBOL);
61
- return face_index(index_base_face_symbol);
62
- }
63
-
64
55
  static int CubeState_replace_face(const VALUE key, const VALUE value, const VALUE self) {
65
56
  const CubeStateData* data;
66
57
  GetInitializedCubeStateData(self, data);
67
- const size_t n = data->cube_size;
58
+ const long n = data->cube_size;
68
59
  Check_Type(value, T_HASH);
69
60
  if (RHASH_SIZE(value) != 3) {
70
61
  rb_raise(rb_eTypeError, "Cube faces must have 3 entries, got %ld.", RHASH_SIZE(value));
@@ -75,19 +66,17 @@ static int CubeState_replace_face(const VALUE key, const VALUE value, const VALU
75
66
  if (stickers == Qnil) {
76
67
  rb_raise(rb_eTypeError, "Cube faces must have a key called :stickers that contains the stickers on that face.");
77
68
  }
78
- const face_index_t x_base_face_index = extract_index_base_face_index(value, ID2SYM(x_base_face_symbol_id));
79
- const face_index_t y_base_face_index = extract_index_base_face_index(value, ID2SYM(y_base_face_symbol_id));
80
69
  Check_Type(stickers, T_ARRAY);
81
70
  if (RARRAY_LEN(stickers) != n) {
82
71
  rb_raise(rb_eArgError, "All faces of a %ldx%ld cube must have %ld rows. Got %ld rows.", n, n, n, RARRAY_LEN(stickers));
83
72
  }
84
- for (size_t y = 0; y < n; ++y) {
73
+ for (long y = 0; y < n; ++y) {
85
74
  const VALUE row = rb_ary_entry(stickers, y);
86
75
  Check_Type(row, T_ARRAY);
87
76
  if (RARRAY_LEN(row) != n) {
88
77
  rb_raise(rb_eArgError, "All rows of a %ldx%ld cube must have %ld cells. Got %ld cells.", n, n, n, RARRAY_LEN(row));
89
78
  }
90
- for (size_t x = 0; x < n; ++x) {
79
+ for (long x = 0; x < n; ++x) {
91
80
  const VALUE cell = rb_ary_entry(row, x);
92
81
  Point point = {x, y};
93
82
  data->stickers[sticker_index(n, on_face_index, point)] = cell;
@@ -99,7 +88,8 @@ static int CubeState_replace_face(const VALUE key, const VALUE value, const VALU
99
88
  static VALUE CubeState_initialize(const VALUE self, const VALUE cube_size, const VALUE stickers) {
100
89
  Check_Type(cube_size, T_FIXNUM);
101
90
  Check_Type(stickers, T_HASH);
102
- const size_t n = FIX2INT(cube_size);
91
+ const long n = FIX2INT(cube_size);
92
+ check_cube_size(n);
103
93
  CubeStateData* data;
104
94
  GetCubeStateData(self, data);
105
95
  data->cube_size = n;
@@ -127,11 +117,11 @@ static VALUE CubeState_sticker_array(const VALUE self,
127
117
  check_base_face_indices(on_face_index, x_base_face_index, y_base_face_index);
128
118
  const CubeStateData* data;
129
119
  GetInitializedCubeStateData(self, data);
130
- const size_t n = data->cube_size;
120
+ const long n = data->cube_size;
131
121
  const VALUE face = rb_ary_new2(n);
132
- for (size_t y = 0; y < n; ++y) {
122
+ for (long y = 0; y < n; ++y) {
133
123
  const VALUE row = rb_ary_new2(n);
134
- for (size_t x = 0; x < n; ++x) {
124
+ for (long x = 0; x < n; ++x) {
135
125
  const Point point = point_on_face(on_face_index, x_base_face_index, y_base_face_index, n, x, y);
136
126
  const VALUE cell = data->stickers[sticker_index(n, on_face_index, point)];
137
127
  rb_ary_store(row, x, cell);
@@ -159,7 +149,7 @@ static VALUE CubeState_hash(const VALUE self) {
159
149
 
160
150
  st_index_t hash = rb_hash_start(data->cube_size);
161
151
  hash = rb_hash_uint(hash, (st_index_t)CubeState_hash);
162
- const size_t n = data->cube_size;
152
+ const long n = data->cube_size;
163
153
  for (size_t i = 0; i < num_stickers(n); i++) {
164
154
  const VALUE sub_hash = rb_hash(data->stickers[i]);
165
155
  hash = rb_hash_uint(hash, NUM2LONG(sub_hash));
@@ -181,7 +171,7 @@ static VALUE CubeState_eql(const VALUE self, const VALUE other) {
181
171
  if (self_data->cube_size != other_data->cube_size) {
182
172
  return Qfalse;
183
173
  }
184
- const size_t n = self_data->cube_size;
174
+ const long n = self_data->cube_size;
185
175
  for (size_t i = 0; i < num_stickers(n); ++i) {
186
176
  if (!color_eq(self_data->stickers[i], other_data->stickers[i])) {
187
177
  return Qfalse;
@@ -193,7 +183,7 @@ static VALUE CubeState_eql(const VALUE self, const VALUE other) {
193
183
  static VALUE CubeState_dup(const VALUE self) {
194
184
  const CubeStateData* data;
195
185
  GetInitializedCubeStateData(self, data);
196
- const size_t n = data->cube_size;
186
+ const long n = data->cube_size;
197
187
  CubeStateData* dupped_data;
198
188
  const VALUE dupped = TypedData_Make_Struct(rb_obj_class(self), CubeStateData, &CubeStateData_type, dupped_data);
199
189
  dupped_data->cube_size = n;
@@ -208,19 +198,19 @@ static VALUE CubeState_cube_size(const VALUE self) {
208
198
  return ST2FIX(data->cube_size);
209
199
  }
210
200
 
211
- void rotate_slice_for_cube(const face_index_t turned_face_index, const size_t slice_index, direction_t direction, const CubeStateData* const data) {
201
+ void rotate_slice_for_cube(const face_index_t turned_face_index, const long slice_index, direction_t direction, const CubeStateData* const data) {
212
202
  direction = CROP_MOD(direction, 4);
213
203
  if (direction == 0) {
214
204
  return;
215
205
  }
216
- const size_t n = data->cube_size;
217
- for (size_t i = 0; i < n; ++i) {
206
+ const long n = data->cube_size;
207
+ for (long x = 0; x < n; ++x) {
218
208
  Sticker4Cycle cycle;
219
- for (size_t j = 0; j < neighbor_faces; ++j) {
220
- const face_index_t on_face_index = neighbor_face_index(turned_face_index, j);
221
- const face_index_t next_face_index = neighbor_face_index(turned_face_index, j + 1);
222
- const Point point = point_on_face(on_face_index, turned_face_index, next_face_index, n, slice_index, i);
223
- cycle.indices[j] = sticker_index(n, on_face_index, point);
209
+ for (size_t i = 0; i < neighbor_faces; ++i) {
210
+ const face_index_t on_face_index = neighbor_face_index(turned_face_index, i);
211
+ const face_index_t next_face_index = neighbor_face_index(turned_face_index, i + 1);
212
+ const Point point = point_on_face(on_face_index, turned_face_index, next_face_index, n, slice_index, x);
213
+ cycle.indices[i] = sticker_index(n, on_face_index, point);
224
214
  }
225
215
  apply_sticker_4cycle(data->stickers, cycle, direction);
226
216
  }
@@ -231,9 +221,9 @@ void rotate_face_for_cube(const face_index_t turned_face_index, direction_t dire
231
221
  if (direction == 0) {
232
222
  return;
233
223
  }
234
- const size_t n = data->cube_size;
235
- for (size_t y = 0; y < n / 2; ++y) {
236
- for (size_t x = 0; x < (n + 1) / 2; ++x) {
224
+ const long n = data->cube_size;
225
+ for (long y = 0; y < n / 2; ++y) {
226
+ for (long x = 0; x < (n + 1) / 2; ++x) {
237
227
  Sticker4Cycle cycle;
238
228
  for (size_t j = 0; j < neighbor_faces; ++j) {
239
229
  const face_index_t x_face_index = neighbor_face_index(turned_face_index, j);
@@ -5,7 +5,7 @@
5
5
  #include "face_symbols.h"
6
6
 
7
7
  typedef struct {
8
- size_t cube_size;
8
+ long cube_size;
9
9
  VALUE* stickers;
10
10
  } CubeStateData;
11
11
 
@@ -24,7 +24,7 @@ extern const rb_data_type_t CubeStateData_type;
24
24
  } \
25
25
  } while(0)
26
26
 
27
- void rotate_slice_for_cube(face_index_t turned_face_index, size_t slice_index, direction_t direction, const CubeStateData* data);
27
+ void rotate_slice_for_cube(face_index_t turned_face_index, long slice_index, direction_t direction, const CubeStateData* data);
28
28
 
29
29
  void rotate_face_for_cube(face_index_t turned_face_index, direction_t direction, const CubeStateData* data);
30
30
 
@@ -4,8 +4,8 @@
4
4
 
5
5
  #include "utils.h"
6
6
 
7
- typedef char face_index_t;
8
- typedef char axis_index_t;
7
+ typedef unsigned char face_index_t;
8
+ typedef unsigned char axis_index_t;
9
9
 
10
10
  #define cube_faces 6
11
11
  #define neighbor_faces 4
@@ -50,15 +50,6 @@ const rb_data_type_t SkewbAlgorithmData_type = {
50
50
  RUBY_TYPED_FREE_IMMEDIATELY
51
51
  };
52
52
 
53
- static void check_moves(const SkewbAlgorithmData* const data, const char* const name) {
54
- for (size_t i = 0; i < data->size; ++i) {
55
- const SkewbMoveType type = data->moves[i].type;
56
- if (type != MOVE && type != ROTATION) {
57
- rb_raise(rb_eRuntimeError, "invalid move type %d in %s", type, name);
58
- }
59
- }
60
- }
61
-
62
53
  static SkewbMove* malloc_moves(const size_t n) {
63
54
  SkewbMove* const moves = malloc(n * sizeof(SkewbMove));
64
55
  if (moves == NULL) {
@@ -122,7 +113,7 @@ static VALUE SkewbAlgorithm_initialize(const VALUE self, const VALUE moves) {
122
113
  data->size = RARRAY_LEN(moves);
123
114
  data->initialized = TRUE;
124
115
  data->moves = malloc_moves(data->size);
125
- for (size_t i = 0; i < RARRAY_LEN(moves); ++i) {
116
+ for (long i = 0; i < RARRAY_LEN(moves); ++i) {
126
117
  const VALUE move = rb_ary_entry(moves, i);
127
118
  if (RARRAY_LEN(move) != 3) {
128
119
  rb_raise(rb_eArgError, "Moves must have 3 elements. Got %ld.", RARRAY_LEN(moves));
@@ -171,8 +162,8 @@ static face_index_t axis_face_on_corner(const Corner corner, const face_index_t
171
162
 
172
163
  bool corners_eq(Corner left, Corner right) {
173
164
  return left.face_indices[0] == right.face_indices[0] &&
174
- (left.face_indices[1] == right.face_indices[1] && left.face_indices[2] == right.face_indices[2] ||
175
- left.face_indices[1] == right.face_indices[2] && left.face_indices[2] == right.face_indices[1]);
165
+ ((left.face_indices[1] == right.face_indices[1] && left.face_indices[2] == right.face_indices[2]) ||
166
+ (left.face_indices[1] == right.face_indices[2] && left.face_indices[2] == right.face_indices[1]));
176
167
  }
177
168
 
178
169
  size_t equivalent_corner_index(const FaceCorners corners, const Corner corner) {
@@ -56,17 +56,6 @@ size_t center_sticker_index(const face_index_t on_face_index) {
56
56
  return on_face_index * skewb_stickers_per_face;
57
57
  }
58
58
 
59
- static VALUE part_type_from_symbol(const VALUE part_type_symbol) {
60
- Check_Type(face_symbol, T_SYMBOL);
61
- if (SYM2ID(part_type_symbol) == center_part_type_id) {
62
- return CENTER;
63
- } else if (SYM2ID(part_type_symbol) == corner_part_type_id) {
64
- return CORNER;
65
- } else {
66
- rb_raise(rb_eArgError, "Invalid part type symbol %+"PRIsVALUE"", part_type_symbol);
67
- }
68
- }
69
-
70
59
  static VALUE part_type_to_symbol(const SkewbPartType part_type) {
71
60
  // Caching these keys isn't easy because the garbage collector will get them.
72
61
  switch (part_type) {
@@ -32,7 +32,7 @@ static ID times = 848;
32
32
 
33
33
  typedef struct {
34
34
  Corner corner_pairs[max_corner_pair_group_size][2];
35
- int num_corner_pairs;
35
+ size_t num_corner_pairs;
36
36
  // Number of different group fingerprints. This can be used to merge several group fingerprints.
37
37
  int num_group_fingerprints;
38
38
  } CornerPairGroup;
@@ -55,7 +55,7 @@ static bool has_color_at(const ActualCornerStickers actual, const size_t index,
55
55
  }
56
56
 
57
57
  typedef struct {
58
- int layer_index;
58
+ size_t layer_index;
59
59
  bool is_oriented;
60
60
  bool is_present;
61
61
  } ActualCornerStickersInfo;
@@ -268,4 +268,5 @@ void init_skewb_layer_fingerprint_method_under(const VALUE module) {
268
268
  plus = rb_intern("+");
269
269
  times = rb_intern("*");
270
270
  rb_define_singleton_method(module, "skewb_layer_fingerprint", skewb_layer_fingerprint, 2);
271
+ init_corner_pair_groups();
271
272
  }
@@ -43,8 +43,6 @@ static int SkewbState_replace_face(const VALUE key, const VALUE value, const VAL
43
43
 
44
44
  static VALUE SkewbState_initialize(const VALUE self, const VALUE stickers) {
45
45
  Check_Type(stickers, T_HASH);
46
- SkewbStateData* data;
47
- GetSkewbStateData(self, data);
48
46
  if (RHASH_SIZE(stickers) != skewb_faces) {
49
47
  rb_raise(rb_eTypeError, "Skewbs must have %d faces. Got %ld.", skewb_faces, RHASH_SIZE(stickers));
50
48
  }
@@ -64,7 +64,7 @@ int log2_64_floor(uint64_t value) {
64
64
  return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58];
65
65
  }
66
66
 
67
- uint64_t iexp(const uint64_t base, uint32_t exp) {
67
+ uint64_t iexp(const uint64_t base, const uint32_t exp) {
68
68
  uint64_t result = 1;
69
69
  for (uint32_t m = 1 << 31; m; m >>= 1) {
70
70
  result = result * result;
@@ -74,3 +74,9 @@ uint64_t iexp(const uint64_t base, uint32_t exp) {
74
74
  }
75
75
  return result;
76
76
  }
77
+
78
+ void check_cube_size(const long cube_size) {
79
+ if (cube_size <= 0) {
80
+ rb_raise(rb_eArgError, "Cube size must be positive. Got %ld.", cube_size);
81
+ }
82
+ }
@@ -5,11 +5,8 @@
5
5
 
6
6
  #define MIN(a, b) ((a) > (b) ? (b) : (a))
7
7
  #define MAX(a, b) ((a) > (b) ? (a) : (b))
8
- #define FALSE 0
9
- #define TRUE 1
10
8
  #define CROP_MOD(a, b) (((a) % (b) + (b)) % (b))
11
9
 
12
- typedef char bool;
13
10
  typedef char direction_t;
14
11
 
15
12
  typedef struct {
@@ -29,3 +26,5 @@ int color_eq(VALUE left, VALUE right);
29
26
  int log2_64_floor(uint64_t value);
30
27
 
31
28
  uint64_t iexp(uint64_t base, uint32_t exp);
29
+
30
+ void check_cube_size(long cube_size);
@@ -143,6 +143,10 @@ module TwistyPuzzles
143
143
  prepend_slice_move(other, cube_size)
144
144
  end
145
145
 
146
+ def prepend_slice_move(_other, _cube_size)
147
+ raise NotImplementedError, "#{self.class}#prepend_slice_move is not implemented"
148
+ end
149
+
146
150
  private
147
151
 
148
152
  def move_count_internal(metric, slice_factor, direction_factor)
@@ -31,14 +31,14 @@ module TwistyPuzzles
31
31
  def parse_move(move_string)
32
32
  match = move_string.match(regexp)
33
33
  if !match || !match.pre_match.empty? || !match.post_match.empty?
34
- raise ArgumentError("Invalid move #{move_string}.")
34
+ raise ArgumentError, "Invalid move #{move_string}."
35
35
  end
36
36
 
37
37
  parsed_parts = parse_named_captures(match)
38
38
  move_type_creators.each do |parser|
39
39
  return parser.create(parsed_parts) if parser.applies_to?(parsed_parts)
40
40
  end
41
- raise "No move type creator applies to #{parsed_parts}"
41
+ raise ArgumentError, "No move type creator applies to #{parsed_parts}"
42
42
  end
43
43
  end
44
44
  end
@@ -4,28 +4,36 @@ require 'twisty_puzzles/cube_direction'
4
4
  require 'twisty_puzzles/rotation'
5
5
 
6
6
  module TwistyPuzzles
7
- AlgorithmTransformation =
8
- Struct.new(:rotation, :mirror, :mirror_normal_face) do
9
- def transformed(algorithm)
10
- algorithm = algorithm.mirror(mirror_normal_face) if mirror
11
- algorithm.rotate_by(rotation)
12
- end
7
+ # A transformation consisting of mirroring and rotating that can be applied to an algorithm.
8
+ class AlgorithmTransformation
9
+ def initialize(rotation, mirror, mirror_normal_face)
10
+ @rotation = rotation
11
+ @mirror = mirror
12
+ @mirror_normal_face = mirror_normal_face
13
+ end
13
14
 
14
- def identity?
15
- rotation.identity? && !mirror
16
- end
15
+ attr_reader :rotation, :mirror, :mirror_normal_face
17
16
 
18
- # Returns algorithm transformations that mirror an algorithm and rotate it around a face.
19
- def self.around_face(face)
20
- around_face_rotations = CubeDirection::ALL_DIRECTIONS.map { |d| Rotation.new(face, d) }
21
- mirror_normal_face = face.neighbors.first
22
- around_face_rotations.product([true, false]).map do |r, m|
23
- AlgorithmTransformation.new(r, m, mirror_normal_face)
24
- end
25
- end
17
+ def transformed(algorithm)
18
+ algorithm = algorithm.mirror(mirror_normal_face) if mirror
19
+ algorithm.rotate_by(rotation)
20
+ end
26
21
 
27
- def self.around_face_without_identity(face)
28
- around_face(face).reject(&:identity?)
22
+ def identity?
23
+ rotation.identity? && !mirror
24
+ end
25
+
26
+ # Returns algorithm transformations that mirror an algorithm and rotate it around a face.
27
+ def self.around_face(face)
28
+ around_face_rotations = CubeDirection::ALL_DIRECTIONS.map { |d| Rotation.new(face, d) }
29
+ mirror_normal_face = face.neighbors.first
30
+ around_face_rotations.product([true, false]).map do |r, m|
31
+ AlgorithmTransformation.new(r, m, mirror_normal_face)
29
32
  end
30
33
  end
34
+
35
+ def self.around_face_without_identity(face)
36
+ around_face(face).reject(&:identity?)
37
+ end
38
+ end
31
39
  end
@@ -55,7 +55,9 @@ module TwistyPuzzles
55
55
  self
56
56
  else
57
57
  rotation_neighbors = rotation.axis_face.neighbors
58
- face_index = rotation_neighbors.index(@axis_face) || raise
58
+ face_index = rotation_neighbors.index(@axis_face)
59
+ raise unless face_index
60
+
59
61
  new_axis_face =
60
62
  rotation_neighbors[(face_index + rotation.direction.value) % rotation_neighbors.length]
61
63
  fields = replace_once(identifying_fields, @axis_face, new_axis_face)
@@ -37,7 +37,7 @@ module TwistyPuzzles
37
37
  end
38
38
 
39
39
  def part_for_colors(part_type, colors)
40
- raise ArgumentError unless part_type.is_a?(Class)
40
+ raise ArgumentError unless part_type.is_a?(Class) && (part_type < Part)
41
41
 
42
42
  part_type.for_face_symbols(colors.map { |c| face_symbol(c) })
43
43
  end
@@ -95,10 +95,12 @@ module TwistyPuzzles
95
95
 
96
96
  def chirality_corner_source_and_unknown_index(obvious_turned_face_symbols_to_colors)
97
97
  corner_matcher =
98
- CornerMatcher.new(CHIRALITY_FACE_SYMBOLS.map do |s|
99
- # This will return nil for exactly one face that we don't know yet.
100
- @colors_to_face_symbols[obvious_turned_face_symbols_to_colors[s]]
101
- end)
98
+ CornerMatcher.new(
99
+ CHIRALITY_FACE_SYMBOLS.map do |s|
100
+ # This will return nil for exactly one face that we don't know yet.
101
+ @colors_to_face_symbols[obvious_turned_face_symbols_to_colors[s]]
102
+ end
103
+ )
102
104
 
103
105
  # There should be exactly one corner that gets mapped to the chirality corner.
104
106
  chirality_corner_source =
@@ -141,7 +143,7 @@ module TwistyPuzzles
141
143
  "Color #{c} cannot be part of the color scheme because it is a reserved color."
142
144
  end
143
145
  end
144
- raise ArgumentError unless face_symbols_to_colors.values.all? { |c| c.is_a?(Symbol) }
146
+ raise ArgumentError unless face_symbols_to_colors.values.all?(Symbol)
145
147
  end
146
148
 
147
149
  def add_missing_mappings(turned_face_symbols_to_colors, chirality_corner_source, unknown_index)
@@ -10,6 +10,10 @@ module TwistyPuzzles
10
10
  def cancellations(other, cube_size, metric = :htm)
11
11
  algorithm.cancellations(other.algorithm, cube_size, metric)
12
12
  end
13
+
14
+ def algorithm
15
+ raise NotImplementedError
16
+ end
13
17
  end
14
18
 
15
19
  # Algorithm that is used like a commutator but actually isn't one.
@@ -6,6 +6,7 @@ require 'twisty_puzzles/native'
6
6
 
7
7
  module TwistyPuzzles
8
8
  # Coordinate of a sticker on the cube.
9
+ # rubocop:disable Metrics/ClassLength
9
10
  class Coordinate
10
11
  def self.highest_coordinate(cube_size)
11
12
  cube_size - 1
@@ -121,6 +122,23 @@ module TwistyPuzzles
121
122
  from_indices(face, cube_size, m, m)
122
123
  end
123
124
 
125
+ def self.face(face, cube_size)
126
+ neighbor_a, neighbor_b = face.neighbors[0..1]
127
+ coordinate_range(cube_size).collect_concat do |x|
128
+ coordinate_range(cube_size).map do |y|
129
+ from_face_distances(face, cube_size, neighbor_a => x, neighbor_b => y)
130
+ end
131
+ end
132
+ end
133
+
134
+ def self.layer(face, cube_size)
135
+ face.neighbors.zip(face.neighbors.rotate(1)).collect_concat do |neighbor, next_neighbor|
136
+ coordinate_range(cube_size).map do |i|
137
+ from_face_distances(neighbor, cube_size, face => 0, next_neighbor => i)
138
+ end
139
+ end + self.face(face, cube_size)
140
+ end
141
+
124
142
  def self.edges_outside(face, cube_size)
125
143
  face.neighbors.zip(face.neighbors.rotate(1)).collect_concat do |neighbor, next_neighbor|
126
144
  1.upto(cube_size - 2).map do |i|
@@ -261,6 +279,7 @@ module TwistyPuzzles
261
279
  rots
262
280
  end
263
281
  end
282
+ # rubocop:enable Metrics/ClassLength
264
283
 
265
284
  # Coordinate of a sticker on the Skewb.
266
285
  class SkewbCoordinate
@@ -64,10 +64,18 @@ module TwistyPuzzles
64
64
  true
65
65
  end
66
66
 
67
+ def num_incarnations(_cube_size)
68
+ 1
69
+ end
70
+
67
71
  def base_index_on_face(cube_size, incarnation_index)
68
72
  base_index_on_other_face(solved_face, cube_size, incarnation_index)
69
73
  end
70
74
 
75
+ def base_index_on_other_face(face, cube_size, incarnation_index)
76
+ raise NotImplementedError
77
+ end
78
+
71
79
  def self.for_face_symbols_internal(face_symbols)
72
80
  raise unless face_symbols.length == self::FACES
73
81
 
@@ -82,6 +90,10 @@ module TwistyPuzzles
82
90
  self::ELEMENTS[index]
83
91
  end
84
92
 
93
+ def self.valid?(_face_symbols)
94
+ false
95
+ end
96
+
85
97
  def <=>(other)
86
98
  @piece_index <=> other.piece_index
87
99
  end
@@ -110,7 +122,7 @@ module TwistyPuzzles
110
122
  # Rotate a piece such that the given face symbol is the first face symbol.
111
123
  def rotate_face_symbol_up(face_symbol)
112
124
  index = @face_symbols.index(face_symbol)
113
- raise "Part #{self} doesn't have face symbol #{c}." unless index
125
+ raise "Part #{self} doesn't have face symbol #{face_symbol}." unless index
114
126
 
115
127
  rotate_by(index)
116
128
  end
@@ -133,10 +145,6 @@ module TwistyPuzzles
133
145
  (0...@face_symbols.length).map { |i| rotate_by(i) }
134
146
  end
135
147
 
136
- def self.create_for_face_symbols(face_symbols)
137
- new(face_symbols)
138
- end
139
-
140
148
  def self.parse(piece_description)
141
149
  face_symbols =
142
150
  piece_description.upcase.strip.split('').map do |e|
@@ -338,10 +346,6 @@ module TwistyPuzzles
338
346
  find_only(self::ELEMENTS) { |e| e.corresponding_part == corresponding_part }
339
347
  end
340
348
 
341
- def self.create_for_face_symbols(face_symbols)
342
- new(self::CORRESPONDING_PART_CLASS.create_for_face_symbols(face_symbols))
343
- end
344
-
345
349
  def face_symbol
346
350
  @face_symbols[0]
347
351
  end
@@ -514,7 +518,7 @@ module TwistyPuzzles
514
518
  end
515
519
 
516
520
  def num_incarnations(cube_size)
517
- [cube_size / 2 - 1, 0].max
521
+ cube_size > 3 ? cube_size / 2 - 1 : 0
518
522
  end
519
523
 
520
524
  # One index of such a piece on a on a NxN face.
@@ -530,14 +534,6 @@ module TwistyPuzzles
530
534
  class Corner < Part
531
535
  FACES = 3
532
536
 
533
- def self.create_for_face_symbols(face_symbols)
534
- piece_candidates =
535
- face_symbols[1..].permutation.map do |cs|
536
- new([face_symbols[0]] + cs)
537
- end
538
- find_only(piece_candidates, &:valid?)
539
- end
540
-
541
537
  def self.for_face_symbols(face_symbols)
542
538
  unless face_symbols.length == FACES
543
539
  raise "Invalid number of face_symbols to create a corner: #{face_symbols.inspect}"
@@ -621,7 +617,7 @@ module TwistyPuzzles
621
617
  ELEMENTS = generate_parts
622
618
 
623
619
  def num_incarnations(cube_size)
624
- [cube_size / 2 - 1, 0].max
620
+ cube_size > 3 ? cube_size / 2 - 1 : 0
625
621
  end
626
622
 
627
623
  # One index of such a piece on a on a NxN face.
@@ -644,10 +640,10 @@ module TwistyPuzzles
644
640
  end
645
641
 
646
642
  def num_incarnations(cube_size)
647
- if cube_size.even?
643
+ if cube_size.even? || cube_size <= 3
648
644
  0
649
645
  else
650
- [cube_size / 2 - 1, 0].max
646
+ cube_size / 2 - 1
651
647
  end
652
648
  end
653
649
 
@@ -75,6 +75,16 @@ module TwistyPuzzles
75
75
 
76
76
  alias == eql?
77
77
 
78
+ def rotation_algorithms
79
+ Rotation::ALL_ROTATIONS.combination(2).reject do |r0, r1|
80
+ r0.inverse == r1
81
+ end.map { |rs| Algorithm.new(rs) } # rubocop:disable Style/MultilineBlockChain
82
+ end
83
+
84
+ def equal_modulo_rotations?(other)
85
+ rotation_algorithms.any? { |r| r.apply_temporarily_to(self) { |state| state == other } }
86
+ end
87
+
78
88
  def hash
79
89
  @hash ||= [self.class, @native].hash
80
90
  end
@@ -6,7 +6,7 @@ module TwistyPuzzles
6
6
  class MoveTypeCreator
7
7
  def initialize(capture_keys, move_class)
8
8
  raise TypeError unless move_class.is_a?(Class)
9
- raise TypeError unless capture_keys.all? { |k| k.is_a?(Symbol) }
9
+ raise TypeError unless capture_keys.all?(Symbol)
10
10
 
11
11
  @capture_keys = capture_keys.freeze
12
12
  @move_class = move_class
@@ -35,10 +35,12 @@ module TwistyPuzzles
35
35
 
36
36
  direction = translated_direction(skewb_direction)
37
37
 
38
- Algorithm.new([
39
- Rotation.new(corner.faces[skewb_direction.value], direction),
40
- Rotation.new(corner.faces[0], direction)
41
- ])
38
+ Algorithm.new(
39
+ [
40
+ Rotation.new(corner.faces[skewb_direction.value], direction),
41
+ Rotation.new(corner.faces[0], direction)
42
+ ]
43
+ )
42
44
  end
43
45
 
44
46
  def to_s
@@ -26,10 +26,12 @@ module TwistyPuzzles
26
26
  next unless c2.face_symbols.include?(c1_rot.face_symbols.first)
27
27
 
28
28
  c2_rot = c2.rotate_face_symbol_up(c1_rot.face_symbols.first)
29
- check_parts.push([
30
- SkewbCoordinate.for_corner(c1_rot),
31
- SkewbCoordinate.for_corner(c2_rot)
32
- ])
29
+ check_parts.push(
30
+ [
31
+ SkewbCoordinate.for_corner(c1_rot),
32
+ SkewbCoordinate.for_corner(c2_rot)
33
+ ]
34
+ )
33
35
  end
34
36
  matching_corners.push(check_parts)
35
37
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TwistyPuzzles
4
- VERSION = '0.0.6'
4
+ VERSION = '0.0.11'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twisty_puzzles
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernhard F. Brodowsky
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-21 00:00:00.000000000 Z
11
+ date: 2021-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -164,7 +164,7 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
- description:
167
+ description:
168
168
  email: bernhard.brodowsky@gmail.com
169
169
  executables: []
170
170
  extensions:
@@ -245,7 +245,7 @@ metadata:
245
245
  homepage_uri: https://github.com/Lykos/twisty_puzzles
246
246
  source_code_uri: https://github.com/Lykos/twisty_puzzles
247
247
  changelog_uri: https://github.com/Lykos/twisty_puzzles/blob/master/CHANGELOG.md
248
- post_install_message:
248
+ post_install_message:
249
249
  rdoc_options: []
250
250
  require_paths:
251
251
  - lib
@@ -253,15 +253,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
253
253
  requirements:
254
254
  - - ">="
255
255
  - !ruby/object:Gem::Version
256
- version: 2.6.0
256
+ version: 3.0.0
257
257
  required_rubygems_version: !ruby/object:Gem::Requirement
258
258
  requirements:
259
259
  - - ">="
260
260
  - !ruby/object:Gem::Version
261
261
  version: '0'
262
262
  requirements: []
263
- rubygems_version: 3.1.2
264
- signing_key:
263
+ rubygems_version: 3.2.3
264
+ signing_key:
265
265
  specification_version: 4
266
266
  summary: Gem for my cube_trainer rails app. Some things are better left in a separate
267
267
  gem with no rails, e.g. native extensions. The main purpose is to support my Rails