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 +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
|
+
}
|