twisty_puzzles 0.0.2 → 0.0.3

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: 989d528a58cb32ca49f7502249b5e5eca71a1e46726f1a79ffa64e18381d5a86
4
- data.tar.gz: 2c1bfff00d66c4b85f15592ee50fa6883e45149ae8456c7a24cc1cba60841285
3
+ metadata.gz: 2ade419b71ffb378baf9543f4971d2ebf303f309c7b22281c24448725c7066e4
4
+ data.tar.gz: 06b797f7652f9138983cb750ddb1dfedddfe1e9fd793f6912b005445ec099687
5
5
  SHA512:
6
- metadata.gz: 870c957e8ba192090401e9e6092779e5f19f45fcd995d60ab1c2e52fed62b7cd25397ce06f18fb2eae6f4ac3df88e76975390f0b73ebc45a8fe2ecdc2cae41fd
7
- data.tar.gz: 6140ebc4150d89e0577461b66b18158dec51c11d3527d265f028d24cd2b1e44f2299b05ae46a3264bbc040dcf0b34bda83be7ec15092884234539130a402d3ef
6
+ metadata.gz: b10baf651145e999a998938806a3e07a10e84f0c060924ca93a898b936b3299d00330f106bc658596c6df50c30a3db4c82c7b80254f535d69d3dc40e3e593424
7
+ data.tar.gz: 262a1c10e10096be3a5f6376679436e8e76cc695ae98a4093a8eadaa2a23aa541c3608c3ace56d231204516880a7f306fced862eb4b179ec608d8eebd02a8201
data/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.0.2]
8
+ ### Fixed
9
+ Added extension source files to files.
10
+
7
11
  ## [0.0.2]
8
12
  ### Changed
9
13
  - Now a simple `require 'twisty_puzzles'` is enough, users don't need to require files separately.
@@ -11,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
15
  ### Fixed
12
16
  - Syntax error in file that wasn't included in tests previously.
13
17
  - Rubocop fixes across the codebase.
14
-
18
+
15
19
  ## [0.0.1]
16
20
  ### Added
17
21
  - Split off core twisty puzzles functionality from cube_trainer repo into a Gem.
@@ -0,0 +1,267 @@
1
+ #include "cube_algorithm.h"
2
+
3
+ #include "face_symbols.h"
4
+ #include "cube_coordinate.h"
5
+ #include "cube_state.h"
6
+ #include "utils.h"
7
+
8
+ static ID slice_id;
9
+ static ID face_id;
10
+ static VALUE CubeAlgorithmClass = Qnil;
11
+
12
+ typedef enum {
13
+ SLICE,
14
+ FACE,
15
+ } CubeMoveType;
16
+
17
+ typedef struct {
18
+ CubeMoveType type;
19
+ face_index_t axis_face_index;
20
+ direction_t direction;
21
+ size_t slice_index;
22
+ } CubeMove;
23
+
24
+ typedef struct {
25
+ size_t size;
26
+ size_t cube_size;
27
+ CubeMove* moves;
28
+ } CubeAlgorithmData;
29
+
30
+ static void CubeAlgorithmData_free(void* const ptr) {
31
+ const CubeAlgorithmData* const data = ptr;
32
+ free(data->moves);
33
+ free(ptr);
34
+ }
35
+
36
+ static size_t CubeAlgorithmData_size(const void* const ptr) {
37
+ const CubeAlgorithmData* const data = ptr;
38
+ return sizeof(CubeAlgorithmData) + data->size * sizeof(CubeMove);
39
+ }
40
+
41
+ const rb_data_type_t CubeAlgorithmData_type = {
42
+ "TwistyPuzzles::Native::CubeAlgorithmData",
43
+ {NULL, CubeAlgorithmData_free, CubeAlgorithmData_size, NULL},
44
+ NULL, NULL,
45
+ RUBY_TYPED_FREE_IMMEDIATELY
46
+ };
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
+ static CubeMove* malloc_moves(const size_t n) {
58
+ CubeMove* const moves = malloc(n * sizeof(CubeMove));
59
+ if (moves == NULL) {
60
+ rb_raise(rb_eNoMemError, "Allocating cube algorithm failed.");
61
+ }
62
+ return moves;
63
+ }
64
+
65
+ static VALUE CubeAlgorithm_alloc(const VALUE klass) {
66
+ CubeAlgorithmData* data;
67
+ const VALUE object = TypedData_Make_Struct(klass, CubeAlgorithmData, &CubeAlgorithmData_type, data);
68
+ data->size = 0;
69
+ data->cube_size = 0;
70
+ data->moves = NULL;
71
+ return object;
72
+ }
73
+
74
+ #define GetCubeAlgorithmData(obj, data) \
75
+ do { \
76
+ TypedData_Get_Struct((obj), CubeAlgorithmData, &CubeAlgorithmData_type, (data)); \
77
+ } while (0)
78
+
79
+ #define GetInitializedCubeAlgorithmData(obj, data) \
80
+ do { \
81
+ GetCubeAlgorithmData((obj), (data)); \
82
+ if (data->cube_size == 0) { \
83
+ rb_raise(rb_eRuntimeError, "Cube algorithm isn't initialized."); \
84
+ } \
85
+ } while(0)
86
+
87
+ static CubeMoveType extract_move_type(const VALUE move_symbol) {
88
+ Check_Type(move_symbol, T_SYMBOL);
89
+ const ID move_symbol_id = SYM2ID(move_symbol);
90
+ if (move_symbol_id == slice_id) {
91
+ return SLICE;
92
+ } else if (move_symbol_id == face_id) {
93
+ return FACE;
94
+ } else {
95
+ rb_raise(rb_eArgError, "Got invalid move symbol.");
96
+ }
97
+ }
98
+
99
+ static size_t components_for_move_type(const CubeMoveType type) {
100
+ switch (type) {
101
+ case SLICE:
102
+ return 4;
103
+ case FACE:
104
+ return 3;
105
+ default:
106
+ rb_raise(rb_eRuntimeError, "invalid move type %d in components_for_move_type", type);
107
+ }
108
+ }
109
+
110
+ static VALUE CubeAlgorithm_initialize(const VALUE self, const VALUE cube_size, const VALUE moves) {
111
+ Check_Type(moves, T_ARRAY);
112
+ CubeAlgorithmData* data;
113
+ GetCubeAlgorithmData(self, data);
114
+ data->size = RARRAY_LEN(moves);
115
+ data->cube_size = NUM2INT(cube_size);
116
+ data->moves = malloc_moves(data->size);
117
+ for (size_t i = 0; i < RARRAY_LEN(moves); ++i) {
118
+ const VALUE move = rb_ary_entry(moves, i);
119
+ if (RARRAY_LEN(move) < 1) {
120
+ rb_raise(rb_eArgError, "Moves cannot be empty.");
121
+ }
122
+ const CubeMoveType type = extract_move_type(rb_ary_entry(move, 0));
123
+ const size_t num_components = components_for_move_type(type);
124
+ if (RARRAY_LEN(move) != num_components) {
125
+ rb_raise(rb_eArgError, "Moves with the given type need to have %ld elements. Got %ld.", num_components, RARRAY_LEN(move));
126
+ }
127
+ data->moves[i].type = type;
128
+ data->moves[i].axis_face_index = face_index(rb_ary_entry(move, 1));
129
+ data->moves[i].direction = NUM2INT(rb_ary_entry(move, 2));
130
+ if (type == SLICE) {
131
+ const size_t slice_index = NUM2INT(rb_ary_entry(move, 3));
132
+ if (slice_index >= data->cube_size) {
133
+ rb_raise(rb_eArgError, "Invalid slice index %ld for cube size %ld.", slice_index, data->cube_size);
134
+ }
135
+ data->moves[i].slice_index = slice_index;
136
+ }
137
+ }
138
+ return self;
139
+ }
140
+
141
+ static void apply_move_to(const CubeMove move, const CubeStateData* const cube_state) {
142
+ switch (move.type) {
143
+ case SLICE:
144
+ rotate_slice_for_cube(move.axis_face_index, move.slice_index, move.direction, cube_state);
145
+ break;
146
+ case FACE:
147
+ rotate_face_for_cube(move.axis_face_index, move.direction, cube_state);
148
+ break;
149
+ default:
150
+ rb_raise(rb_eRuntimeError, "invalid move type %d in apply_move_to", move.type);
151
+ }
152
+ }
153
+
154
+ static VALUE CubeAlgorithm_apply_to(const VALUE self, const VALUE cube_state) {
155
+ const CubeStateData* cube_state_data;
156
+ GetInitializedCubeStateData(cube_state, cube_state_data);
157
+ const CubeAlgorithmData* data;
158
+ GetInitializedCubeAlgorithmData(self, data);
159
+ for (size_t i = 0; i < data->size; ++i) {
160
+ apply_move_to(data->moves[i], cube_state_data);
161
+ }
162
+ return Qnil;
163
+ }
164
+
165
+ static CubeMove rotate_move_by(const CubeMove move, const face_index_t rotation_face_index, const direction_t rotation_direction) {
166
+ CubeMove result = move;
167
+ if (!same_axis(move.axis_face_index, rotation_face_index)) {
168
+ const size_t index = neighbor_index(rotation_face_index, move.axis_face_index);
169
+ result.axis_face_index = neighbor_face_index(rotation_face_index, index + rotation_direction);
170
+ }
171
+ return result;
172
+ }
173
+
174
+ static VALUE CubeAlgorithm_rotate_by(const VALUE self, const VALUE rotation_face_symbol, const VALUE direction) {
175
+ const face_index_t rotation_face_index = face_index(rotation_face_symbol);
176
+ const direction_t rotation_direction = NUM2INT(direction);
177
+ const CubeAlgorithmData* data;
178
+ GetInitializedCubeAlgorithmData(self, data);
179
+ CubeAlgorithmData* rotated_data;
180
+ const VALUE rotated = TypedData_Make_Struct(CubeAlgorithmClass, CubeAlgorithmData, &CubeAlgorithmData_type, rotated_data);
181
+ rotated_data->size = data->size;
182
+ rotated_data->cube_size = data->cube_size;
183
+ rotated_data->moves = malloc_moves(rotated_data->size);
184
+ for (size_t i = 0; i < data->size; ++i) {
185
+ rotated_data->moves[i] = rotate_move_by(data->moves[i], rotation_face_index, rotation_direction);
186
+ }
187
+ return rotated;
188
+ }
189
+
190
+ static CubeMove mirror_move(const CubeMove move, const face_index_t normal_face_index) {
191
+ CubeMove result = move;
192
+ if (same_axis(move.axis_face_index, normal_face_index)) {
193
+ result.axis_face_index = opposite_face_index(move.axis_face_index);
194
+ }
195
+ result.direction = invert_cube_direction(move.direction);
196
+ return result;
197
+ }
198
+
199
+ static VALUE CubeAlgorithm_mirror(const VALUE self, const VALUE normal_face_symbol) {
200
+ const face_index_t normal_face_index = face_index(normal_face_symbol);
201
+ const CubeAlgorithmData* data;
202
+ GetInitializedCubeAlgorithmData(self, data);
203
+ CubeAlgorithmData* mirrored_data;
204
+ const VALUE mirrored = TypedData_Make_Struct(CubeAlgorithmClass, CubeAlgorithmData, &CubeAlgorithmData_type, mirrored_data);
205
+ mirrored_data->size = data->size;
206
+ mirrored_data->cube_size = data->cube_size;
207
+ mirrored_data->moves = malloc_moves(mirrored_data->size);
208
+ for (size_t i = 0; i < data->size; ++i) {
209
+ mirrored_data->moves[i] = mirror_move(data->moves[i], normal_face_index);
210
+ }
211
+ return mirrored;
212
+ }
213
+
214
+ static CubeMove invert_move(const CubeMove move) {
215
+ CubeMove result = move;
216
+ result.direction = invert_cube_direction(result.direction);
217
+ return result;
218
+ }
219
+
220
+ static VALUE CubeAlgorithm_inverse(const VALUE self) {
221
+ const CubeAlgorithmData* data;
222
+ GetInitializedCubeAlgorithmData(self, data);
223
+ CubeAlgorithmData* inverted_data;
224
+ const VALUE inverted = TypedData_Make_Struct(CubeAlgorithmClass, CubeAlgorithmData, &CubeAlgorithmData_type, inverted_data);
225
+ inverted_data->size = data->size;
226
+ inverted_data->cube_size = data->cube_size;
227
+ inverted_data->moves = malloc_moves(inverted_data->size);
228
+ for (size_t i = 0; i < data->size; ++i) {
229
+ inverted_data->moves[i] = invert_move(data->moves[data->size - 1 - i]);
230
+ }
231
+ return inverted;
232
+ }
233
+
234
+ static VALUE CubeAlgorithm_plus(const VALUE self, const VALUE other) {
235
+ const CubeAlgorithmData* self_data;
236
+ GetInitializedCubeAlgorithmData(self, self_data);
237
+ const CubeAlgorithmData* other_data;
238
+ GetInitializedCubeAlgorithmData(other, other_data);
239
+ if (self_data->cube_size != other_data->cube_size) {
240
+ rb_raise(rb_eArgError, "Cannot concatenate algorithms for different cube sizes %ld and %ld.", self_data->cube_size, other_data->cube_size);
241
+ }
242
+ CubeAlgorithmData* sum_data;
243
+ const VALUE sum = TypedData_Make_Struct(CubeAlgorithmClass, CubeAlgorithmData, &CubeAlgorithmData_type, sum_data);
244
+ sum_data->size = self_data->size + other_data->size;
245
+ sum_data->cube_size = self_data->cube_size;
246
+ sum_data->moves = malloc_moves(sum_data->size);
247
+ for (size_t i = 0; i < self_data->size; ++i) {
248
+ sum_data->moves[i] = self_data->moves[i];
249
+ }
250
+ for (size_t i = 0; i < other_data->size; ++i) {
251
+ sum_data->moves[self_data->size + i] = other_data->moves[i];
252
+ }
253
+ return sum;
254
+ }
255
+
256
+ void init_cube_algorithm_class_under(const VALUE module) {
257
+ slice_id = rb_intern("slice");
258
+ face_id = rb_intern("face");
259
+ CubeAlgorithmClass = rb_define_class_under(module, "CubeAlgorithm", rb_cObject);
260
+ rb_define_alloc_func(CubeAlgorithmClass, CubeAlgorithm_alloc);
261
+ rb_define_method(CubeAlgorithmClass, "initialize", CubeAlgorithm_initialize, 2);
262
+ rb_define_method(CubeAlgorithmClass, "apply_to", CubeAlgorithm_apply_to, 1);
263
+ rb_define_method(CubeAlgorithmClass, "rotate_by", CubeAlgorithm_rotate_by, 2);
264
+ rb_define_method(CubeAlgorithmClass, "mirror", CubeAlgorithm_mirror, 1);
265
+ rb_define_method(CubeAlgorithmClass, "inverse", CubeAlgorithm_inverse, 0);
266
+ rb_define_method(CubeAlgorithmClass, "+", CubeAlgorithm_plus, 1);
267
+ }
@@ -0,0 +1,5 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ void init_cube_algorithm_class_under(VALUE module);
@@ -0,0 +1,184 @@
1
+ #include "cube_average.h"
2
+
3
+ #include <stdlib.h>
4
+
5
+ #include "utils.h"
6
+
7
+ VALUE CubeAverageClass = Qnil;
8
+ const double removed_fraction_per_side = 0.05;
9
+
10
+ typedef struct {
11
+ size_t capacity;
12
+ size_t size;
13
+ size_t insert_index;
14
+ double* values;
15
+ double average;
16
+ } CubeAverageData;
17
+
18
+ static size_t CubeAverageData_size(const void* const ptr) {
19
+ return sizeof(CubeAverageData);
20
+ }
21
+
22
+ const rb_data_type_t CubeAverageData_type = {
23
+ "TwistyPuzzles::Native::CubeAverageData",
24
+ {NULL, NULL, CubeAverageData_size, NULL},
25
+ NULL, NULL,
26
+ RUBY_TYPED_FREE_IMMEDIATELY
27
+ };
28
+
29
+ static double* malloc_values(const size_t n) {
30
+ double* const values = malloc(n * sizeof(double));
31
+ if (values == NULL) {
32
+ rb_raise(rb_eNoMemError, "Allocating values failed.");
33
+ }
34
+ return values;
35
+ }
36
+
37
+ #define GetCubeAverageData(obj, data) \
38
+ do { \
39
+ TypedData_Get_Struct((obj), CubeAverageData, &CubeAverageData_type, (data)); \
40
+ } while (0)
41
+ #define GetInitializedCubeAverageData(obj, data) \
42
+ do { \
43
+ GetCubeAverageData((obj), (data)); \
44
+ if (data->values == NULL) { \
45
+ rb_raise(rb_eArgError, "Cube average isn't initialized."); \
46
+ } \
47
+ } while (0)
48
+
49
+ static VALUE CubeAverage_alloc(const VALUE klass) {
50
+ CubeAverageData* data;
51
+ const VALUE object = TypedData_Make_Struct(klass, CubeAverageData, &CubeAverageData_type, data);
52
+ data->capacity = 0;
53
+ data->size = 0;
54
+ data->insert_index = 0;
55
+ data->values = NULL;
56
+ data->average = NAN;
57
+ return object;
58
+ }
59
+
60
+ static VALUE CubeAverage_initialize(const VALUE self, const VALUE capacity, const VALUE initial_average) {
61
+ Check_Type(capacity, T_FIXNUM);
62
+ const size_t n = FIX2INT(capacity);
63
+ if (n < 3) {
64
+ rb_raise(rb_eArgError, "The number of elements for a cube average has to be at least 3. Got %ld.", n);
65
+ }
66
+ if (n > 1000) {
67
+ rb_raise(rb_eArgError, "The number of elements for a cube average can be at most 1000, otherwise we need a better implementation. Got %ld.", n);
68
+ }
69
+
70
+ CubeAverageData* data;
71
+ GetCubeAverageData(self, data);
72
+
73
+ data->capacity = n;
74
+ data->size = 0;
75
+ data->values = malloc_values(n);
76
+ data->average = NUM2DBL(initial_average);
77
+
78
+ return self;
79
+ }
80
+
81
+ static VALUE CubeAverage_capacity(const VALUE self) {
82
+ const CubeAverageData* data;
83
+ GetInitializedCubeAverageData(self, data);
84
+ return INT2NUM(data->capacity);
85
+ }
86
+
87
+ static VALUE CubeAverage_length(const VALUE self) {
88
+ const CubeAverageData* data;
89
+ GetInitializedCubeAverageData(self, data);
90
+ return INT2NUM(data->size);
91
+ }
92
+
93
+ static int saturated(const CubeAverageData* const data) {
94
+ return data->size == data->capacity;
95
+ }
96
+
97
+ static int comp(const void* left_ptr, const void* right_ptr) {
98
+ const double left = *((double*)left_ptr);
99
+ const double right = *((double*)right_ptr);
100
+ if (left > right) { return 1; }
101
+ if (left < right) { return -1; }
102
+ return 0;
103
+ }
104
+
105
+ static double compute_average(const double* const values, const size_t size) {
106
+ double sum = 0;
107
+ for (size_t i = 0; i < size; ++i) {
108
+ sum += values[i];
109
+ }
110
+ return sum / size;
111
+ }
112
+
113
+ static double compute_cube_average(const double* const values, const size_t size) {
114
+ if (size <= 2) {
115
+ return compute_average(values, size);
116
+ }
117
+ double* const tmp = malloc_values(size);
118
+ memcpy(tmp, values, size * sizeof(double));
119
+ qsort(tmp, size, sizeof(double), comp);
120
+ const size_t num_removed = ceil(size * removed_fraction_per_side);
121
+ const double result = compute_average(tmp + num_removed, size - 2 * num_removed);
122
+ free(tmp);
123
+ return result;
124
+ }
125
+
126
+ static VALUE CubeAverage_push(const VALUE self, const VALUE new_value) {
127
+ CubeAverageData* data;
128
+ GetInitializedCubeAverageData(self, data);
129
+
130
+ data->values[data->insert_index] = NUM2DBL(new_value);
131
+ data->size = MIN(data->size + 1, data->capacity);
132
+ data->insert_index = (data->insert_index + 1) % data->capacity;
133
+ data->average = compute_cube_average(data->values, data->size);
134
+
135
+ return DBL2NUM(data->average);
136
+ }
137
+
138
+ static VALUE CubeAverage_push_all(const VALUE self, const VALUE new_values) {
139
+ Check_Type(new_values, T_ARRAY);
140
+ CubeAverageData* data;
141
+ GetInitializedCubeAverageData(self, data);
142
+
143
+ const size_t num_values = RARRAY_LEN(new_values);
144
+ if (num_values == 0) {
145
+ return DBL2NUM(data->average);
146
+ }
147
+ const size_t insert_index = data->insert_index;
148
+ const size_t capacity = data->capacity;
149
+ const size_t start = num_values > capacity ? num_values - capacity : 0;
150
+ for (size_t i = start; i < RARRAY_LEN(new_values); ++i) {
151
+ const VALUE new_value = rb_ary_entry(new_values, i);
152
+ data->values[(insert_index + i) % capacity] = NUM2DBL(new_value);
153
+ }
154
+
155
+ data->size = MIN(data->size + num_values, capacity);
156
+ data->insert_index = (insert_index + num_values) % capacity;
157
+ data->average = compute_cube_average(data->values, data->size);
158
+
159
+ return DBL2NUM(data->average);
160
+ }
161
+
162
+ static VALUE CubeAverage_saturated(const VALUE self) {
163
+ const CubeAverageData* data;
164
+ GetInitializedCubeAverageData(self, data);
165
+ return saturated(data) ? Qtrue : Qfalse;
166
+ }
167
+
168
+ static VALUE CubeAverage_average(const VALUE self) {
169
+ const CubeAverageData* data;
170
+ GetInitializedCubeAverageData(self, data);
171
+ return DBL2NUM(data->average);
172
+ }
173
+
174
+ void init_cube_average_class_under(const VALUE module) {
175
+ CubeAverageClass = rb_define_class_under(module, "CubeAverage", rb_cObject);
176
+ rb_define_alloc_func(CubeAverageClass, CubeAverage_alloc);
177
+ rb_define_method(CubeAverageClass, "initialize", CubeAverage_initialize, 2);
178
+ rb_define_method(CubeAverageClass, "capacity", CubeAverage_capacity, 0);
179
+ rb_define_method(CubeAverageClass, "length", CubeAverage_length, 0);
180
+ rb_define_method(CubeAverageClass, "push", CubeAverage_push, 1);
181
+ rb_define_method(CubeAverageClass, "push_all", CubeAverage_push_all, 1);
182
+ rb_define_method(CubeAverageClass, "saturated?", CubeAverage_saturated, 0);
183
+ rb_define_method(CubeAverageClass, "average", CubeAverage_average, 0);
184
+ }