twisty_puzzles 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -1
- data/ext/twisty_puzzles/native/cube_algorithm.c +267 -0
- data/ext/twisty_puzzles/native/cube_algorithm.h +5 -0
- data/ext/twisty_puzzles/native/cube_average.c +184 -0
- data/ext/twisty_puzzles/native/cube_average.h +5 -0
- data/ext/twisty_puzzles/native/cube_coordinate.c +207 -0
- data/ext/twisty_puzzles/native/cube_coordinate.h +34 -0
- data/ext/twisty_puzzles/native/cube_state.c +264 -0
- data/ext/twisty_puzzles/native/cube_state.h +31 -0
- data/ext/twisty_puzzles/native/face_symbols.c +67 -0
- data/ext/twisty_puzzles/native/face_symbols.h +34 -0
- data/ext/twisty_puzzles/native/native.c +28 -0
- data/ext/twisty_puzzles/native/skewb_algorithm.c +331 -0
- data/ext/twisty_puzzles/native/skewb_algorithm.h +5 -0
- data/ext/twisty_puzzles/native/skewb_coordinate.c +237 -0
- data/ext/twisty_puzzles/native/skewb_coordinate.h +36 -0
- data/ext/twisty_puzzles/native/skewb_layer_fingerprint.c +271 -0
- data/ext/twisty_puzzles/native/skewb_layer_fingerprint.h +5 -0
- data/ext/twisty_puzzles/native/skewb_state.c +214 -0
- data/ext/twisty_puzzles/native/skewb_state.h +23 -0
- data/ext/twisty_puzzles/native/utils.c +76 -0
- data/ext/twisty_puzzles/native/utils.h +31 -0
- data/lib/twisty_puzzles/version.rb +1 -1
- metadata +22 -1
@@ -0,0 +1,207 @@
|
|
1
|
+
#include "cube_coordinate.h"
|
2
|
+
|
3
|
+
#include <stdio.h>
|
4
|
+
|
5
|
+
static VALUE CubeCoordinateClass = Qnil;
|
6
|
+
|
7
|
+
typedef struct {
|
8
|
+
size_t cube_size;
|
9
|
+
face_index_t on_face_index;
|
10
|
+
Point point;
|
11
|
+
} CubeCoordinateData;
|
12
|
+
|
13
|
+
static size_t CubeCoordinateData_size(const void* const ptr) {
|
14
|
+
return sizeof(CubeCoordinateData);
|
15
|
+
}
|
16
|
+
|
17
|
+
const rb_data_type_t CubeCoordinateData_type = {
|
18
|
+
"TwistyPuzzles::Native::CubeCoordinateData",
|
19
|
+
{NULL, NULL, CubeCoordinateData_size, NULL},
|
20
|
+
NULL, NULL,
|
21
|
+
RUBY_TYPED_FREE_IMMEDIATELY
|
22
|
+
};
|
23
|
+
|
24
|
+
#define GetCubeCoordinateData(obj, data) \
|
25
|
+
do { \
|
26
|
+
TypedData_Get_Struct((obj), CubeCoordinateData, &CubeCoordinateData_type, (data)); \
|
27
|
+
} while (0)
|
28
|
+
|
29
|
+
size_t num_stickers(const size_t cube_size) {
|
30
|
+
return cube_faces * cube_size * cube_size;
|
31
|
+
}
|
32
|
+
|
33
|
+
size_t sticker_index(const size_t cube_size, const face_index_t on_face_index, const Point point) {
|
34
|
+
return on_face_index * cube_size * cube_size + point.y * cube_size + point.x;
|
35
|
+
}
|
36
|
+
|
37
|
+
size_t CubeCoordinate_sticker_index(const VALUE self, const size_t cube_size) {
|
38
|
+
CubeCoordinateData* data;
|
39
|
+
GetCubeCoordinateData(self, data);
|
40
|
+
if (data->cube_size != cube_size) {
|
41
|
+
rb_raise(rb_eArgError, "Cannot use coordinate for cube size %ld on a %ldx%ld cube.", data->cube_size, cube_size, cube_size);
|
42
|
+
}
|
43
|
+
return sticker_index(cube_size, data->on_face_index, data->point);
|
44
|
+
}
|
45
|
+
|
46
|
+
static VALUE CubeCoordinate_alloc(const VALUE klass) {
|
47
|
+
CubeCoordinateData* data;
|
48
|
+
const VALUE object = TypedData_Make_Struct(klass, CubeCoordinateData, &CubeCoordinateData_type, data);
|
49
|
+
data->cube_size = 0;
|
50
|
+
data->on_face_index = 0;
|
51
|
+
data->point.x = 0;
|
52
|
+
data->point.y = 0;
|
53
|
+
return object;
|
54
|
+
}
|
55
|
+
|
56
|
+
static size_t inverted_index(const size_t cube_size, const size_t index) {
|
57
|
+
return cube_size - 1 - index;
|
58
|
+
}
|
59
|
+
|
60
|
+
size_t transform_index(const face_index_t index_base_face_index, const size_t cube_size, const size_t index) {
|
61
|
+
if (index_base_face_index == axis_index(index_base_face_index)) {
|
62
|
+
return index;
|
63
|
+
} else {
|
64
|
+
return inverted_index(cube_size, index);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
bool switch_axes(const face_index_t x_base_face_index, const face_index_t y_base_face_index) {
|
69
|
+
return axis_index(x_base_face_index) > axis_index(y_base_face_index);
|
70
|
+
}
|
71
|
+
|
72
|
+
void check_base_face_indices(const face_index_t on_face_index,
|
73
|
+
const face_index_t x_base_face_index,
|
74
|
+
const face_index_t y_base_face_index) {
|
75
|
+
if (axis_index(x_base_face_index) == axis_index(on_face_index)) {
|
76
|
+
rb_raise(rb_eArgError, "Invalid value for x_base_face_symbol.");
|
77
|
+
}
|
78
|
+
if (axis_index(y_base_face_index) == axis_index(on_face_index)) {
|
79
|
+
rb_raise(rb_eArgError, "Invalid value for y_base_face_symbol.");
|
80
|
+
}
|
81
|
+
if (axis_index(x_base_face_index) == axis_index(y_base_face_index)) {
|
82
|
+
rb_raise(rb_eArgError, "Incompatible values for x_base_face_symbol and y_base_face_symbol.");
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
Point point_on_face(const face_index_t face_index,
|
87
|
+
const face_index_t x_base_face_index,
|
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);
|
94
|
+
Point point;
|
95
|
+
if (switch_axes(x_base_face_index, y_base_face_index)) {
|
96
|
+
point.x = transformed_y;
|
97
|
+
point.y = transformed_x;
|
98
|
+
} else {
|
99
|
+
point.x = transformed_x;
|
100
|
+
point.y = transformed_y;
|
101
|
+
}
|
102
|
+
return point;
|
103
|
+
}
|
104
|
+
|
105
|
+
static void check_cube_index(const size_t cube_size, const size_t index) {
|
106
|
+
if (index < 0 || index >= cube_size) {
|
107
|
+
rb_raise(rb_eArgError, "Invalid value %ld for x with cube size %ld.", index, cube_size);
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
static VALUE CubeCoordinate_initialize(const VALUE self,
|
112
|
+
const VALUE cube_size,
|
113
|
+
const VALUE face_symbol,
|
114
|
+
const VALUE x_base_face_symbol,
|
115
|
+
const VALUE y_base_face_symbol,
|
116
|
+
const VALUE x_num,
|
117
|
+
const VALUE y_num) {
|
118
|
+
Check_Type(cube_size, T_FIXNUM);
|
119
|
+
Check_Type(face_symbol, T_SYMBOL);
|
120
|
+
Check_Type(x_base_face_symbol, T_SYMBOL);
|
121
|
+
Check_Type(y_base_face_symbol, T_SYMBOL);
|
122
|
+
Check_Type(x_num, T_FIXNUM);
|
123
|
+
Check_Type(y_num, T_FIXNUM);
|
124
|
+
const size_t n = FIX2INT(cube_size);
|
125
|
+
const face_index_t on_face_index = face_index(face_symbol);
|
126
|
+
const face_index_t x_base_face_index = face_index(x_base_face_symbol);
|
127
|
+
const face_index_t y_base_face_index = face_index(y_base_face_symbol);
|
128
|
+
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
|
+
check_cube_index(n, untransformed_x);
|
131
|
+
const size_t untransformed_y = FIX2INT(y_num);
|
132
|
+
check_cube_index(n, untransformed_y);
|
133
|
+
const Point point = point_on_face(on_face_index, x_base_face_index, y_base_face_index, n, untransformed_x, untransformed_y);
|
134
|
+
CubeCoordinateData* data;
|
135
|
+
GetCubeCoordinateData(self, data);
|
136
|
+
data->cube_size = n;
|
137
|
+
data->on_face_index = on_face_index;
|
138
|
+
data->point = point;
|
139
|
+
return self;
|
140
|
+
}
|
141
|
+
|
142
|
+
static VALUE CubeCoordinate_hash(const VALUE self) {
|
143
|
+
const CubeCoordinateData* data;
|
144
|
+
GetCubeCoordinateData(self, data);
|
145
|
+
|
146
|
+
st_index_t hash = rb_hash_start((st_index_t)CubeCoordinate_hash);
|
147
|
+
hash = rb_hash_uint(hash, data->cube_size);
|
148
|
+
hash = rb_hash_uint(hash, data->on_face_index);
|
149
|
+
hash = rb_hash_uint(hash, data->point.x);
|
150
|
+
hash = rb_hash_uint(hash, data->point.y);
|
151
|
+
return ST2FIX(rb_hash_end(hash));
|
152
|
+
}
|
153
|
+
|
154
|
+
static VALUE CubeCoordinate_eql(const VALUE self, const VALUE other) {
|
155
|
+
if (self == other) {
|
156
|
+
return Qtrue;
|
157
|
+
}
|
158
|
+
if (rb_obj_class(self) != rb_obj_class(other)) {
|
159
|
+
return Qfalse;
|
160
|
+
}
|
161
|
+
const CubeCoordinateData* self_data;
|
162
|
+
GetCubeCoordinateData(self, self_data);
|
163
|
+
const CubeCoordinateData* other_data;
|
164
|
+
GetCubeCoordinateData(other, other_data);
|
165
|
+
if (self_data->cube_size == other_data->cube_size &&
|
166
|
+
self_data->on_face_index == other_data->on_face_index &&
|
167
|
+
self_data->point.x == other_data->point.x &&
|
168
|
+
self_data->point.y == other_data->point.y) {
|
169
|
+
return Qtrue;
|
170
|
+
} else {
|
171
|
+
return Qfalse;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
static VALUE CubeCoordinate_cube_size(const VALUE self) {
|
176
|
+
const CubeCoordinateData* data;
|
177
|
+
GetCubeCoordinateData(self, data);
|
178
|
+
return INT2NUM(data->cube_size);
|
179
|
+
}
|
180
|
+
|
181
|
+
static VALUE CubeCoordinate_face(const VALUE self) {
|
182
|
+
const CubeCoordinateData* data;
|
183
|
+
GetCubeCoordinateData(self, data);
|
184
|
+
return face_symbol(data->on_face_index);
|
185
|
+
}
|
186
|
+
|
187
|
+
static VALUE CubeCoordinate_coordinate(const VALUE self, const VALUE index_base_face_symbol) {
|
188
|
+
const CubeCoordinateData* data;
|
189
|
+
GetCubeCoordinateData(self, data);
|
190
|
+
const face_index_t index_base_face_index = face_index(index_base_face_symbol);
|
191
|
+
// Make use of the fact that 0 + 1 + 2 = 3
|
192
|
+
const face_index_t third_face_index = 3 - axis_index(data->on_face_index) - axis_index(index_base_face_index);
|
193
|
+
const size_t index = switch_axes(index_base_face_index, third_face_index) ? data->point.y : data->point.x;
|
194
|
+
return INT2NUM(transform_index(index_base_face_index, data->cube_size, index));
|
195
|
+
}
|
196
|
+
|
197
|
+
void init_cube_coordinate_class_under(const VALUE module) {
|
198
|
+
CubeCoordinateClass = rb_define_class_under(module, "CubeCoordinate", rb_cObject);
|
199
|
+
rb_define_alloc_func(CubeCoordinateClass, CubeCoordinate_alloc);
|
200
|
+
rb_define_method(CubeCoordinateClass, "initialize", CubeCoordinate_initialize, 6);
|
201
|
+
rb_define_method(CubeCoordinateClass, "hash", CubeCoordinate_hash, 0);
|
202
|
+
rb_define_method(CubeCoordinateClass, "eql?", CubeCoordinate_eql, 1);
|
203
|
+
rb_define_alias(CubeCoordinateClass, "==", "eql?");
|
204
|
+
rb_define_method(CubeCoordinateClass, "cube_size", CubeCoordinate_cube_size, 0);
|
205
|
+
rb_define_method(CubeCoordinateClass, "face", CubeCoordinate_face, 0);
|
206
|
+
rb_define_method(CubeCoordinateClass, "coordinate", CubeCoordinate_coordinate, 1);
|
207
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <ruby.h>
|
4
|
+
|
5
|
+
#include "face_symbols.h"
|
6
|
+
#include "utils.h"
|
7
|
+
|
8
|
+
size_t num_stickers(size_t cube_size);
|
9
|
+
|
10
|
+
typedef struct {
|
11
|
+
size_t x;
|
12
|
+
size_t y;
|
13
|
+
} Point;
|
14
|
+
|
15
|
+
size_t sticker_index(size_t cube_size, face_index_t on_face_index, Point point);
|
16
|
+
|
17
|
+
size_t transform_index(face_index_t index_base_face_index, size_t cube_size, size_t index);
|
18
|
+
|
19
|
+
bool switch_axes(face_index_t x_base_face_index, face_index_t y_base_face_index);
|
20
|
+
|
21
|
+
void check_base_face_indices(face_index_t on_face_index,
|
22
|
+
face_index_t x_base_face_index,
|
23
|
+
face_index_t y_base_face_index);
|
24
|
+
|
25
|
+
Point point_on_face(face_index_t on_face_index,
|
26
|
+
face_index_t x_base_face_index,
|
27
|
+
face_index_t y_base_face_index,
|
28
|
+
size_t cube_size,
|
29
|
+
size_t untransformed_x,
|
30
|
+
size_t untransformed_y);
|
31
|
+
|
32
|
+
size_t CubeCoordinate_sticker_index(VALUE self, size_t cube_size);
|
33
|
+
|
34
|
+
void init_cube_coordinate_class_under(VALUE module);
|
@@ -0,0 +1,264 @@
|
|
1
|
+
#include "cube_state.h"
|
2
|
+
|
3
|
+
#include <stdio.h>
|
4
|
+
|
5
|
+
#include "cube_coordinate.h"
|
6
|
+
#include "utils.h"
|
7
|
+
|
8
|
+
static ID stickers_id = 0;
|
9
|
+
static ID x_base_face_symbol_id = 0;
|
10
|
+
static ID y_base_face_symbol_id = 0;
|
11
|
+
static VALUE CubeStateClass = Qnil;
|
12
|
+
|
13
|
+
static void CubeStateData_mark(void* const ptr) {
|
14
|
+
const CubeStateData* data = ptr;
|
15
|
+
const size_t n = data->cube_size;
|
16
|
+
for (size_t i = 0; i < num_stickers(n); ++i) {
|
17
|
+
rb_gc_mark(data->stickers[i]);
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
static void CubeStateData_free(void* const ptr) {
|
22
|
+
const CubeStateData* const data = ptr;
|
23
|
+
free(data->stickers);
|
24
|
+
free(ptr);
|
25
|
+
}
|
26
|
+
|
27
|
+
static size_t CubeStateData_size(const void* const ptr) {
|
28
|
+
const CubeStateData* const data = ptr;
|
29
|
+
return sizeof(CubeStateData) + num_stickers(data->cube_size) * sizeof(VALUE);
|
30
|
+
}
|
31
|
+
|
32
|
+
const rb_data_type_t CubeStateData_type = {
|
33
|
+
"TwistyPuzzles::Native::CubeStateData",
|
34
|
+
{CubeStateData_mark, CubeStateData_free, CubeStateData_size, NULL},
|
35
|
+
NULL, NULL,
|
36
|
+
RUBY_TYPED_FREE_IMMEDIATELY
|
37
|
+
};
|
38
|
+
|
39
|
+
static VALUE* malloc_stickers(const size_t n) {
|
40
|
+
VALUE* const stickers = malloc(num_stickers(n) * sizeof(VALUE));
|
41
|
+
if (stickers == NULL) {
|
42
|
+
rb_raise(rb_eNoMemError, "Allocating cube state failed.");
|
43
|
+
}
|
44
|
+
return stickers;
|
45
|
+
}
|
46
|
+
|
47
|
+
static VALUE CubeState_alloc(const VALUE klass) {
|
48
|
+
CubeStateData* data;
|
49
|
+
const VALUE object = TypedData_Make_Struct(klass, CubeStateData, &CubeStateData_type, data);
|
50
|
+
data->cube_size = 0;
|
51
|
+
data->stickers = NULL;
|
52
|
+
return object;
|
53
|
+
}
|
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
|
+
static int CubeState_replace_face(const VALUE key, const VALUE value, const VALUE self) {
|
65
|
+
const CubeStateData* data;
|
66
|
+
GetInitializedCubeStateData(self, data);
|
67
|
+
const size_t n = data->cube_size;
|
68
|
+
Check_Type(value, T_HASH);
|
69
|
+
if (RHASH_SIZE(value) != 3) {
|
70
|
+
rb_raise(rb_eTypeError, "Cube faces must have 3 entries, got %ld.", RHASH_SIZE(value));
|
71
|
+
}
|
72
|
+
const face_index_t on_face_index = face_index(key);
|
73
|
+
// Caching these keys isn't easy because the garbage collector will get them.
|
74
|
+
const VALUE stickers = rb_hash_aref(value, ID2SYM(stickers_id));
|
75
|
+
if (stickers == Qnil) {
|
76
|
+
rb_raise(rb_eTypeError, "Cube faces must have a key called :stickers that contains the stickers on that face.");
|
77
|
+
}
|
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
|
+
Check_Type(stickers, T_ARRAY);
|
81
|
+
if (RARRAY_LEN(stickers) != n) {
|
82
|
+
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
|
+
}
|
84
|
+
for (size_t y = 0; y < n; ++y) {
|
85
|
+
const VALUE row = rb_ary_entry(stickers, y);
|
86
|
+
Check_Type(row, T_ARRAY);
|
87
|
+
if (RARRAY_LEN(row) != n) {
|
88
|
+
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
|
+
}
|
90
|
+
for (size_t x = 0; x < n; ++x) {
|
91
|
+
const VALUE cell = rb_ary_entry(row, x);
|
92
|
+
Point point = {x, y};
|
93
|
+
data->stickers[sticker_index(n, on_face_index, point)] = cell;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
return ST_CONTINUE;
|
97
|
+
}
|
98
|
+
|
99
|
+
static VALUE CubeState_initialize(const VALUE self, const VALUE cube_size, const VALUE stickers) {
|
100
|
+
Check_Type(cube_size, T_FIXNUM);
|
101
|
+
Check_Type(stickers, T_HASH);
|
102
|
+
const size_t n = FIX2INT(cube_size);
|
103
|
+
CubeStateData* data;
|
104
|
+
GetCubeStateData(self, data);
|
105
|
+
data->cube_size = n;
|
106
|
+
data->stickers = malloc_stickers(n);
|
107
|
+
for (size_t i = 0; i < num_stickers(n); ++i) {
|
108
|
+
data->stickers[i] = Qnil;
|
109
|
+
}
|
110
|
+
if (RHASH_SIZE(stickers) != cube_faces) {
|
111
|
+
rb_raise(rb_eTypeError, "Cubes must have %d faces. Got %ld.", cube_faces, RHASH_SIZE(stickers));
|
112
|
+
}
|
113
|
+
rb_hash_foreach(stickers, CubeState_replace_face, self);
|
114
|
+
return self;
|
115
|
+
}
|
116
|
+
|
117
|
+
static VALUE CubeState_sticker_array(const VALUE self,
|
118
|
+
const VALUE on_face_symbol,
|
119
|
+
const VALUE x_base_face_symbol,
|
120
|
+
const VALUE y_base_face_symbol) {
|
121
|
+
Check_Type(on_face_symbol, T_SYMBOL);
|
122
|
+
Check_Type(x_base_face_symbol, T_SYMBOL);
|
123
|
+
Check_Type(y_base_face_symbol, T_SYMBOL);
|
124
|
+
const face_index_t on_face_index = face_index(on_face_symbol);
|
125
|
+
const face_index_t x_base_face_index = face_index(x_base_face_symbol);
|
126
|
+
const face_index_t y_base_face_index = face_index(y_base_face_symbol);
|
127
|
+
check_base_face_indices(on_face_index, x_base_face_index, y_base_face_index);
|
128
|
+
const CubeStateData* data;
|
129
|
+
GetInitializedCubeStateData(self, data);
|
130
|
+
const size_t n = data->cube_size;
|
131
|
+
const VALUE face = rb_ary_new2(n);
|
132
|
+
for (size_t y = 0; y < n; ++y) {
|
133
|
+
const VALUE row = rb_ary_new2(n);
|
134
|
+
for (size_t x = 0; x < n; ++x) {
|
135
|
+
const Point point = point_on_face(on_face_index, x_base_face_index, y_base_face_index, n, x, y);
|
136
|
+
const VALUE cell = data->stickers[sticker_index(n, on_face_index, point)];
|
137
|
+
rb_ary_store(row, x, cell);
|
138
|
+
}
|
139
|
+
rb_ary_store(face, y, row);
|
140
|
+
}
|
141
|
+
return face;
|
142
|
+
}
|
143
|
+
|
144
|
+
static VALUE CubeState_entry(const VALUE self, const VALUE coordinate) {
|
145
|
+
const CubeStateData* data;
|
146
|
+
GetInitializedCubeStateData(self, data);
|
147
|
+
return data->stickers[CubeCoordinate_sticker_index(coordinate, data->cube_size)];
|
148
|
+
}
|
149
|
+
|
150
|
+
static VALUE CubeState_store(const VALUE self, const VALUE coordinate, const VALUE value) {
|
151
|
+
const CubeStateData* data;
|
152
|
+
GetInitializedCubeStateData(self, data);
|
153
|
+
return data->stickers[CubeCoordinate_sticker_index(coordinate, data->cube_size)] = value;
|
154
|
+
}
|
155
|
+
|
156
|
+
static VALUE CubeState_hash(const VALUE self) {
|
157
|
+
const CubeStateData* data;
|
158
|
+
GetInitializedCubeStateData(self, data);
|
159
|
+
|
160
|
+
st_index_t hash = rb_hash_start(data->cube_size);
|
161
|
+
hash = rb_hash_uint(hash, (st_index_t)CubeState_hash);
|
162
|
+
const size_t n = data->cube_size;
|
163
|
+
for (size_t i = 0; i < num_stickers(n); i++) {
|
164
|
+
const VALUE sub_hash = rb_hash(data->stickers[i]);
|
165
|
+
hash = rb_hash_uint(hash, NUM2LONG(sub_hash));
|
166
|
+
}
|
167
|
+
return ST2FIX(rb_hash_end(hash));
|
168
|
+
}
|
169
|
+
|
170
|
+
static VALUE CubeState_eql(const VALUE self, const VALUE other) {
|
171
|
+
if (self == other) {
|
172
|
+
return Qtrue;
|
173
|
+
}
|
174
|
+
if (rb_obj_class(self) != rb_obj_class(other)) {
|
175
|
+
return Qfalse;
|
176
|
+
}
|
177
|
+
const CubeStateData* self_data;
|
178
|
+
GetInitializedCubeStateData(self, self_data);
|
179
|
+
const CubeStateData* other_data;
|
180
|
+
GetInitializedCubeStateData(other, other_data);
|
181
|
+
if (self_data->cube_size != other_data->cube_size) {
|
182
|
+
return Qfalse;
|
183
|
+
}
|
184
|
+
const size_t n = self_data->cube_size;
|
185
|
+
for (size_t i = 0; i < num_stickers(n); ++i) {
|
186
|
+
if (!color_eq(self_data->stickers[i], other_data->stickers[i])) {
|
187
|
+
return Qfalse;
|
188
|
+
}
|
189
|
+
}
|
190
|
+
return Qtrue;
|
191
|
+
}
|
192
|
+
|
193
|
+
static VALUE CubeState_dup(const VALUE self) {
|
194
|
+
const CubeStateData* data;
|
195
|
+
GetInitializedCubeStateData(self, data);
|
196
|
+
const size_t n = data->cube_size;
|
197
|
+
CubeStateData* dupped_data;
|
198
|
+
const VALUE dupped = TypedData_Make_Struct(rb_obj_class(self), CubeStateData, &CubeStateData_type, dupped_data);
|
199
|
+
dupped_data->cube_size = n;
|
200
|
+
dupped_data->stickers = malloc_stickers(n);
|
201
|
+
memcpy(dupped_data->stickers, data->stickers, num_stickers(n) * sizeof(VALUE));
|
202
|
+
return dupped;
|
203
|
+
}
|
204
|
+
|
205
|
+
static VALUE CubeState_cube_size(const VALUE self) {
|
206
|
+
const CubeStateData* data;
|
207
|
+
GetInitializedCubeStateData(self, data);
|
208
|
+
return ST2FIX(data->cube_size);
|
209
|
+
}
|
210
|
+
|
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) {
|
212
|
+
direction = CROP_MOD(direction, 4);
|
213
|
+
if (direction == 0) {
|
214
|
+
return;
|
215
|
+
}
|
216
|
+
const size_t n = data->cube_size;
|
217
|
+
for (size_t i = 0; i < n; ++i) {
|
218
|
+
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);
|
224
|
+
}
|
225
|
+
apply_sticker_4cycle(data->stickers, cycle, direction);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
void rotate_face_for_cube(const face_index_t turned_face_index, direction_t direction, const CubeStateData* const data) {
|
230
|
+
direction = CROP_MOD(direction, 4);
|
231
|
+
if (direction == 0) {
|
232
|
+
return;
|
233
|
+
}
|
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) {
|
237
|
+
Sticker4Cycle cycle;
|
238
|
+
for (size_t j = 0; j < neighbor_faces; ++j) {
|
239
|
+
const face_index_t x_face_index = neighbor_face_index(turned_face_index, j);
|
240
|
+
const face_index_t y_face_index = neighbor_face_index(turned_face_index, j + 1);
|
241
|
+
const Point point = point_on_face(turned_face_index, x_face_index, y_face_index, n, x, y);
|
242
|
+
cycle.indices[j] = sticker_index(n, turned_face_index, point);
|
243
|
+
}
|
244
|
+
apply_sticker_4cycle(data->stickers, cycle, direction);
|
245
|
+
}
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
void init_cube_state_class_under(const VALUE module) {
|
250
|
+
stickers_id = rb_intern("stickers");
|
251
|
+
x_base_face_symbol_id = rb_intern("x_base_face_symbol");
|
252
|
+
y_base_face_symbol_id = rb_intern("y_base_face_symbol");
|
253
|
+
CubeStateClass = rb_define_class_under(module, "CubeState", rb_cObject);
|
254
|
+
rb_define_alloc_func(CubeStateClass, CubeState_alloc);
|
255
|
+
rb_define_method(CubeStateClass, "initialize", CubeState_initialize, 2);
|
256
|
+
rb_define_method(CubeStateClass, "[]", CubeState_entry, 1);
|
257
|
+
rb_define_method(CubeStateClass, "[]=", CubeState_store, 2);
|
258
|
+
rb_define_method(CubeStateClass, "sticker_array", CubeState_sticker_array, 3);
|
259
|
+
rb_define_method(CubeStateClass, "hash", CubeState_hash, 0);
|
260
|
+
rb_define_method(CubeStateClass, "eql?", CubeState_eql, 1);
|
261
|
+
rb_define_alias(CubeStateClass, "==", "eql?");
|
262
|
+
rb_define_method(CubeStateClass, "dup", CubeState_dup, 0);
|
263
|
+
rb_define_method(CubeStateClass, "cube_size", CubeState_cube_size, 0);
|
264
|
+
}
|