twisty_puzzles 0.0.4 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +6 -1
- data/ext/twisty_puzzles/native/cube_algorithm.c +10 -18
- data/ext/twisty_puzzles/native/cube_average.c +2 -2
- data/ext/twisty_puzzles/native/cube_coordinate.c +16 -15
- data/ext/twisty_puzzles/native/cube_coordinate.h +7 -7
- data/ext/twisty_puzzles/native/cube_state.c +23 -33
- data/ext/twisty_puzzles/native/cube_state.h +2 -2
- data/ext/twisty_puzzles/native/face_symbols.h +2 -2
- data/ext/twisty_puzzles/native/skewb_algorithm.c +3 -12
- data/ext/twisty_puzzles/native/skewb_coordinate.c +0 -11
- data/ext/twisty_puzzles/native/skewb_layer_fingerprint.c +3 -2
- data/ext/twisty_puzzles/native/skewb_state.c +0 -2
- data/ext/twisty_puzzles/native/utils.c +7 -1
- data/ext/twisty_puzzles/native/utils.h +2 -3
- data/lib/twisty_puzzles.rb +1 -0
- data/lib/twisty_puzzles/abstract_move.rb +1 -2
- data/lib/twisty_puzzles/abstract_move_parser.rb +1 -1
- data/lib/twisty_puzzles/algorithm.rb +1 -1
- data/lib/twisty_puzzles/axis_face_and_direction_move.rb +1 -0
- data/lib/twisty_puzzles/cancellation_helper.rb +1 -1
- data/lib/twisty_puzzles/color_scheme.rb +1 -1
- data/lib/twisty_puzzles/commutator.rb +3 -0
- data/lib/twisty_puzzles/compiled_algorithm.rb +4 -4
- data/lib/twisty_puzzles/coordinate.rb +23 -6
- data/lib/twisty_puzzles/cube.rb +3 -3
- data/lib/twisty_puzzles/cube_move.rb +0 -4
- data/lib/twisty_puzzles/cube_move_parser.rb +23 -23
- data/lib/twisty_puzzles/cube_print_helper.rb +4 -3
- data/lib/twisty_puzzles/parser.rb +3 -3
- data/lib/twisty_puzzles/rotation.rb +2 -0
- data/lib/twisty_puzzles/skewb_move.rb +1 -0
- data/lib/twisty_puzzles/skewb_move_parser.rb +1 -0
- data/lib/twisty_puzzles/skewb_notation.rb +7 -1
- data/lib/twisty_puzzles/version.rb +1 -1
- metadata +11 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3dfb23949825572c531ac7bfcea491151629f8673160605069b1793a827d961a
|
4
|
+
data.tar.gz: f343cc589e99ac129717f47a7b3dbe4dcb3d4a0cbcae2e0f8c42b12b22ad842c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2fc20aef94e333318c53ee292b5418424ad8880cc2f8ff18c2102d0799d96728ccc9a98869269bf9c6a046174dcf5f71182cf6498021838d8772c18a7008cfe2
|
7
|
+
data.tar.gz: b79344d44bf6fd32cd9443c79405dcab052867a746e23fecc0b1496bd96b36fc6c4dd09d2a8c3d33389d7e5b5daaf0ca4a0552cce8948c59ac590da458c6150d
|
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.5]
|
8
|
+
### Changed
|
9
|
+
- Now `require 'twisty_puzzles'` also requires the native extensions.
|
10
|
+
|
7
11
|
## [0.0.4]
|
8
12
|
### Fixed
|
9
13
|
Fixed path in extconf.
|
data/README.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
![Ruby](https://github.com/Lykos/twisty_puzzles/workflows/Ruby/badge.svg)
|
2
|
+
![Rubocop](https://github.com/Lykos/twisty_puzzles/workflows/Rubocop/badge.svg)
|
3
|
+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/twisty_puzzles.svg)](https://badge.fury.io/rb/twisty_puzzles)
|
5
|
+
|
1
6
|
# Twisty Puzzles
|
2
7
|
Gem for my cube_trainer rails app. Some things are better left in a separate gem with no rails, e.g. native extensions. The main purpose is to support my Rails app, but if it's useful for someone else, feel free to use it at your own risk.
|
3
8
|
|
@@ -25,7 +30,7 @@ TODO: Write usage instructions here
|
|
25
30
|
|
26
31
|
After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bundle exec bin/console` for an interactive prompt that will allow you to experiment.
|
27
32
|
|
28
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
33
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `lib/twisty_puzzles/version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
29
34
|
|
30
35
|
## Contributing
|
31
36
|
|
@@ -18,12 +18,12 @@ typedef struct {
|
|
18
18
|
CubeMoveType type;
|
19
19
|
face_index_t axis_face_index;
|
20
20
|
direction_t direction;
|
21
|
-
|
21
|
+
long slice_index;
|
22
22
|
} CubeMove;
|
23
23
|
|
24
24
|
typedef struct {
|
25
25
|
size_t size;
|
26
|
-
|
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
|
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 =
|
106
|
+
data->cube_size = FIX2INT(cube_size);
|
107
|
+
check_cube_size(data->cube_size);
|
116
108
|
data->moves = malloc_moves(data->size);
|
117
|
-
for (
|
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
|
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 =
|
121
|
+
data->moves[i].direction = FIX2INT(rb_ary_entry(move, 2));
|
130
122
|
if (type == SLICE) {
|
131
|
-
const
|
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
|
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 (
|
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
|
-
|
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
|
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
|
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
|
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
|
56
|
+
static long inverted_index(const long cube_size, const long index) {
|
57
57
|
return cube_size - 1 - index;
|
58
58
|
}
|
59
59
|
|
60
|
-
|
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
|
90
|
-
const
|
91
|
-
const
|
92
|
-
const
|
93
|
-
const
|
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
|
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
|
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
|
130
|
+
const long untransformed_x = FIX2INT(x_num);
|
130
131
|
check_cube_index(n, untransformed_x);
|
131
|
-
const
|
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(
|
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(
|
15
|
+
size_t sticker_index(long cube_size, face_index_t on_face_index, Point point);
|
16
16
|
|
17
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
28
|
+
long cube_size,
|
29
|
+
long untransformed_x,
|
30
|
+
long untransformed_y);
|
31
31
|
|
32
|
-
size_t CubeCoordinate_sticker_index(VALUE self,
|
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
|
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
|
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 (
|
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 (
|
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
|
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
|
120
|
+
const long n = data->cube_size;
|
131
121
|
const VALUE face = rb_ary_new2(n);
|
132
|
-
for (
|
122
|
+
for (long y = 0; y < n; ++y) {
|
133
123
|
const VALUE row = rb_ary_new2(n);
|
134
|
-
for (
|
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
|
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
|
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
|
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
|
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
|
217
|
-
for (
|
206
|
+
const long n = data->cube_size;
|
207
|
+
for (long x = 0; x < n; ++x) {
|
218
208
|
Sticker4Cycle cycle;
|
219
|
-
for (size_t
|
220
|
-
const face_index_t on_face_index = neighbor_face_index(turned_face_index,
|
221
|
-
const face_index_t next_face_index = neighbor_face_index(turned_face_index,
|
222
|
-
const Point point = point_on_face(on_face_index, turned_face_index, next_face_index, n, slice_index,
|
223
|
-
cycle.indices[
|
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
|
235
|
-
for (
|
236
|
-
for (
|
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
|
-
|
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,
|
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
|
|
@@ -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 (
|
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
|
-
|
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
|
-
|
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);
|
data/lib/twisty_puzzles.rb
CHANGED
@@ -22,6 +22,7 @@ require 'twisty_puzzles/cube_print_helper'
|
|
22
22
|
require 'twisty_puzzles/cube_state'
|
23
23
|
require 'twisty_puzzles/letter_scheme'
|
24
24
|
require 'twisty_puzzles/move_type_creator'
|
25
|
+
require 'twisty_puzzles/native'
|
25
26
|
require 'twisty_puzzles/parser'
|
26
27
|
require 'twisty_puzzles/part_cycle_factory'
|
27
28
|
require 'twisty_puzzles/puzzle'
|
@@ -150,8 +150,7 @@ module TwistyPuzzles
|
|
150
150
|
when :qtm then slice_factor * direction_factor
|
151
151
|
when :htm then slice_factor
|
152
152
|
when :stm then 1
|
153
|
-
when :qstm then direction_factor
|
154
|
-
when :sqtm then direction_factor
|
153
|
+
when :qstm, :sqtm then direction_factor
|
155
154
|
else raise ArgumentError, "Invalid move metric #{metric.inspect}."
|
156
155
|
end
|
157
156
|
end
|
@@ -20,7 +20,7 @@ module TwistyPuzzles
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def parse_named_captures(match)
|
23
|
-
present_named_captures = match.named_captures.
|
23
|
+
present_named_captures = match.named_captures.compact
|
24
24
|
present_named_captures.map do |name, string|
|
25
25
|
key = parse_part_key(name).to_sym
|
26
26
|
value = parse_move_part(name, string)
|
@@ -135,7 +135,7 @@ module TwistyPuzzles
|
|
135
135
|
def self.alg_plus_cancelled_move(algorithm, move, cube_size)
|
136
136
|
if move.is_a?(Rotation) && (tail_rotations = num_tail_rotations(algorithm)) >= 2
|
137
137
|
Algorithm.new(algorithm.moves[0...-tail_rotations]) +
|
138
|
-
cancelled_rotations(algorithm.moves[-tail_rotations
|
138
|
+
cancelled_rotations(algorithm.moves[-tail_rotations..] + [move])
|
139
139
|
else
|
140
140
|
Algorithm.new(algorithm.moves[0...-1]) +
|
141
141
|
algorithm.moves[-1].join_with_cancellation(move, cube_size)
|
@@ -56,7 +56,7 @@ module TwistyPuzzles
|
|
56
56
|
raise ArgumentError unless colors.include?(top_color)
|
57
57
|
raise ArgumentError unless colors.include?(front_color)
|
58
58
|
|
59
|
-
#
|
59
|
+
# NOTE: The reason that this is so complicated is that we want it to still work if the
|
60
60
|
# chirality corner gets exchanged.
|
61
61
|
|
62
62
|
# Do the obvious and handle opposites of the top and front color so we have no
|
@@ -17,6 +17,7 @@ module TwistyPuzzles
|
|
17
17
|
def initialize(algorithm)
|
18
18
|
raise ArgumentError unless algorithm.is_a?(Algorithm)
|
19
19
|
|
20
|
+
super()
|
20
21
|
@algorithm = algorithm
|
21
22
|
end
|
22
23
|
|
@@ -47,6 +48,7 @@ module TwistyPuzzles
|
|
47
48
|
raise ArgumentError unless first_part.is_a?(Algorithm)
|
48
49
|
raise ArgumentError unless second_part.is_a?(Algorithm)
|
49
50
|
|
51
|
+
super()
|
50
52
|
@first_part = first_part
|
51
53
|
@second_part = second_part
|
52
54
|
end
|
@@ -85,6 +87,7 @@ module TwistyPuzzles
|
|
85
87
|
raise ArgumentError, 'Inner commutator has to be a commutator.'
|
86
88
|
end
|
87
89
|
|
90
|
+
super()
|
88
91
|
@setup = setup
|
89
92
|
@inner_commutator = inner_commutator
|
90
93
|
end
|
@@ -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
|
@@ -90,7 +91,7 @@ module TwistyPuzzles
|
|
90
91
|
base_coordinate = Coordinate.from_indices(
|
91
92
|
part.solved_face, cube_size, *part.base_index_on_face(cube_size, incarnation_index)
|
92
93
|
)
|
93
|
-
other_face_symbols = part.corresponding_part.face_symbols[1
|
94
|
+
other_face_symbols = part.corresponding_part.face_symbols[1..]
|
94
95
|
match_coordinate_internal(base_coordinate, other_face_symbols)
|
95
96
|
end
|
96
97
|
|
@@ -99,7 +100,7 @@ module TwistyPuzzles
|
|
99
100
|
def self.solved_positions(part, cube_size, incarnation_index)
|
100
101
|
solved_coordinate = solved_position(part, cube_size, incarnation_index)
|
101
102
|
other_coordinates =
|
102
|
-
part.face_symbols[1
|
103
|
+
part.face_symbols[1..].map.with_index do |f, i|
|
103
104
|
face = Face.for_face_symbol(f)
|
104
105
|
# The reverse is important for edge like parts. We are not in the same position as usual
|
105
106
|
# solved pieces would be.
|
@@ -109,7 +110,7 @@ module TwistyPuzzles
|
|
109
110
|
base_coordinate = Coordinate.from_indices(face, cube_size, *base_indices)
|
110
111
|
other_face_symbols = [part.face_symbols[0]] +
|
111
112
|
part.corresponding_part.face_symbols[1...i + 1] +
|
112
|
-
part.corresponding_part.face_symbols[i + 2
|
113
|
+
part.corresponding_part.face_symbols[i + 2..]
|
113
114
|
match_coordinate_internal(base_coordinate, other_face_symbols)
|
114
115
|
end
|
115
116
|
[solved_coordinate] + other_coordinates
|
@@ -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
|
@@ -276,7 +295,7 @@ module TwistyPuzzles
|
|
276
295
|
@native = native
|
277
296
|
end
|
278
297
|
|
279
|
-
attr_reader :native
|
298
|
+
attr_reader :native, :coordinate
|
280
299
|
|
281
300
|
private_class_method :new
|
282
301
|
|
@@ -311,7 +330,5 @@ module TwistyPuzzles
|
|
311
330
|
def face
|
312
331
|
@face ||= Face.for_face_symbol(@native.face)
|
313
332
|
end
|
314
|
-
|
315
|
-
attr_reader :coordinate
|
316
333
|
end
|
317
334
|
end
|
data/lib/twisty_puzzles/cube.rb
CHANGED
@@ -97,7 +97,7 @@ module TwistyPuzzles
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def inspect
|
100
|
-
self.class
|
100
|
+
"#{self.class}(#{@face_symbols.map(&:to_s).join(', ')})"
|
101
101
|
end
|
102
102
|
|
103
103
|
def to_s
|
@@ -365,7 +365,7 @@ module TwistyPuzzles
|
|
365
365
|
attr_reader :corresponding_part
|
366
366
|
|
367
367
|
def inspect
|
368
|
-
self.class
|
368
|
+
"#{self.class}(#{face_symbol}, #{@corresponding_part.inspect})"
|
369
369
|
end
|
370
370
|
|
371
371
|
def rotate_by(_number)
|
@@ -532,7 +532,7 @@ module TwistyPuzzles
|
|
532
532
|
|
533
533
|
def self.create_for_face_symbols(face_symbols)
|
534
534
|
piece_candidates =
|
535
|
-
face_symbols[1
|
535
|
+
face_symbols[1..].permutation.map do |cs|
|
536
536
|
new([face_symbols[0]] + cs)
|
537
537
|
end
|
538
538
|
find_only(piece_candidates, &:valid?)
|
@@ -139,8 +139,6 @@ module TwistyPuzzles
|
|
139
139
|
end
|
140
140
|
end
|
141
141
|
|
142
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
143
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
144
142
|
def prepend_fat_move(other, cube_size)
|
145
143
|
if same_fat_block?(other)
|
146
144
|
merge_with_same_fat_block(other)
|
@@ -156,8 +154,6 @@ module TwistyPuzzles
|
|
156
154
|
Algorithm.move(other.inner_fat_mslice_move(cube_size))
|
157
155
|
end
|
158
156
|
end
|
159
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
160
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
161
157
|
|
162
158
|
def prepend_slice_move(other, cube_size)
|
163
159
|
return unless same_axis?(other)
|
@@ -14,29 +14,29 @@ module TwistyPuzzles
|
|
14
14
|
class CubeMoveParser < AbstractMoveParser
|
15
15
|
REGEXP =
|
16
16
|
begin
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
17
|
+
axes_part = "(?<axis_name>[#{AbstractMove::AXES.join}])"
|
18
|
+
face_names = CubeConstants::FACE_NAMES.join
|
19
|
+
fat_move_part =
|
20
|
+
"(?<width>\\d*)(?<fat_face_name>[#{face_names}])w"
|
21
|
+
normal_move_part = "(?<face_name>[#{face_names}])"
|
22
|
+
downcased_face_names = face_names.downcase
|
23
|
+
maybe_fat_maybe_slice_move_part =
|
24
|
+
"(?<maybe_fat_face_maybe_slice_name>[#{downcased_face_names}])"
|
25
|
+
slice_move_part =
|
26
|
+
"(?<slice_index>\\d+)(?<slice_name>[#{downcased_face_names}])"
|
27
|
+
mslice_move_part =
|
28
|
+
"(?<mslice_name>[#{AbstractMove::SLICE_FACES.keys.join}])"
|
29
|
+
move_part = "(?:#{axes_part}|" \
|
30
|
+
"#{fat_move_part}|" \
|
31
|
+
"#{normal_move_part}|" \
|
32
|
+
"#{maybe_fat_maybe_slice_move_part}|" \
|
33
|
+
"#{slice_move_part}|#{mslice_move_part})"
|
34
|
+
direction_names =
|
35
|
+
AbstractDirection::POSSIBLE_DIRECTION_NAMES.flatten
|
36
|
+
direction_names.sort_by! { |e| -e.length }
|
37
|
+
direction_part = "(?<direction>#{direction_names.join('|')})"
|
38
|
+
Regexp.new("#{move_part}#{direction_part}")
|
39
|
+
end
|
40
40
|
|
41
41
|
MOVE_TYPE_CREATORS = [
|
42
42
|
MoveTypeCreator.new(%i[axis_face direction], Rotation),
|
@@ -41,9 +41,10 @@ module TwistyPuzzles
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def maybe_reverse(reverse_mode, stuff)
|
44
|
-
|
44
|
+
case reverse_mode
|
45
|
+
when :reverse
|
45
46
|
stuff.reverse
|
46
|
-
|
47
|
+
when :keep
|
47
48
|
stuff
|
48
49
|
else
|
49
50
|
raise
|
@@ -102,7 +103,7 @@ module TwistyPuzzles
|
|
102
103
|
face_symbol_info = FACE_SYMBOL_INFOS[face_symbol]
|
103
104
|
stickers = cube_state.sticker_array(face)
|
104
105
|
center_color = color_character(stickers[0], color_mode)
|
105
|
-
corner_colors = stickers[1
|
106
|
+
corner_colors = stickers[1..].map { |c| color_character(c, color_mode) }
|
106
107
|
permuted_corner_colors =
|
107
108
|
apply_permutation(corner_colors, face_symbol_info.skewb_corner_permutation)
|
108
109
|
raise unless corner_colors.length == 4
|
@@ -185,14 +185,14 @@ module TwistyPuzzles # rubocop:disable Style/Documentation
|
|
185
185
|
end
|
186
186
|
end
|
187
187
|
|
188
|
-
def parse_commutator(alg_string, complete_parse
|
188
|
+
def parse_commutator(alg_string, complete_parse: true)
|
189
189
|
parser = Parser.new(alg_string, CubeMoveParser::INSTANCE)
|
190
190
|
commutator = parser.parse_commutator
|
191
191
|
parser.check_eos('commutator') if complete_parse
|
192
192
|
commutator
|
193
193
|
end
|
194
194
|
|
195
|
-
def parse_cube_algorithm(alg_string, complete_parse
|
195
|
+
def parse_cube_algorithm(alg_string, complete_parse: true)
|
196
196
|
parser = Parser.new(alg_string, CubeMoveParser::INSTANCE)
|
197
197
|
algorithm = parser.parse_algorithm
|
198
198
|
parser.check_eos('algorithm') if complete_parse
|
@@ -206,7 +206,7 @@ module TwistyPuzzles # rubocop:disable Style/Documentation
|
|
206
206
|
alias parse_algorithm parse_cube_algorithm
|
207
207
|
alias parse_move parse_cube_move
|
208
208
|
|
209
|
-
def parse_skewb_algorithm(alg_string, notation, complete_parse
|
209
|
+
def parse_skewb_algorithm(alg_string, notation, complete_parse: true)
|
210
210
|
parser = Parser.new(alg_string, SkewbMoveParser.new(notation))
|
211
211
|
algorithm = parser.parse_algorithm
|
212
212
|
parser.check_eos('algorithm') if complete_parse
|
@@ -62,6 +62,7 @@ module TwistyPuzzles
|
|
62
62
|
[self, alternative].include?(other)
|
63
63
|
end
|
64
64
|
|
65
|
+
# rubocop:disable Metrics/AbcSize
|
65
66
|
def prepend_rotation(other, _cube_size)
|
66
67
|
if same_axis?(other)
|
67
68
|
direction = translated_direction(other.axis_face)
|
@@ -74,6 +75,7 @@ module TwistyPuzzles
|
|
74
75
|
Algorithm.move(Rotation.new(remaining_face, CubeDirection::DOUBLE))
|
75
76
|
end
|
76
77
|
end
|
78
|
+
# rubocop:enable Metrics/AbcSize
|
77
79
|
|
78
80
|
def prepend_fat_m_slice_move(_other, _cube_size)
|
79
81
|
nil
|
@@ -12,6 +12,7 @@ module TwistyPuzzles
|
|
12
12
|
raise TypeError unless axis_corner.is_a?(Corner)
|
13
13
|
raise TypeError unless direction.is_a?(SkewbDirection)
|
14
14
|
|
15
|
+
super()
|
15
16
|
@axis_corner = axis_corner.rotate_face_up(axis_corner.faces.min_by(&:piece_index))
|
16
17
|
@direction = direction
|
17
18
|
end
|
@@ -10,9 +10,11 @@ module TwistyPuzzles
|
|
10
10
|
# Class that represents one notation for Skewb moves, e.g. Sarahs notation or fixed
|
11
11
|
# corner notation.
|
12
12
|
class SkewbNotation
|
13
|
+
# rubocop:disable Metrics/AbcSize
|
13
14
|
def initialize(name, move_corner_pairs)
|
14
15
|
raise TypeError unless name.is_a?(String)
|
15
16
|
|
17
|
+
super()
|
16
18
|
check_move_corner_pairs(move_corner_pairs)
|
17
19
|
@name = name
|
18
20
|
@move_to_corner = move_corner_pairs.to_h.freeze
|
@@ -26,7 +28,9 @@ module TwistyPuzzles
|
|
26
28
|
end.freeze
|
27
29
|
freeze
|
28
30
|
end
|
31
|
+
# rubocop:enable Metrics/AbcSize
|
29
32
|
|
33
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
30
34
|
def check_move_corner_pairs(move_corner_pairs)
|
31
35
|
move_corner_pairs.each do |m|
|
32
36
|
raise ArgumentError unless m.length == 2
|
@@ -37,6 +41,7 @@ module TwistyPuzzles
|
|
37
41
|
|
38
42
|
check_corner_coverage(move_corner_pairs.map(&:last))
|
39
43
|
end
|
44
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
40
45
|
|
41
46
|
def check_corner_coverage(corners)
|
42
47
|
corner_closure = corners + corners.map(&:diagonal_opposite)
|
@@ -49,6 +54,7 @@ module TwistyPuzzles
|
|
49
54
|
end
|
50
55
|
|
51
56
|
attr_reader :name, :move_strings, :non_zero_moves
|
57
|
+
|
52
58
|
private_class_method :new
|
53
59
|
|
54
60
|
def self.fixed_corner
|
@@ -103,7 +109,7 @@ module TwistyPuzzles
|
|
103
109
|
move_to_string(m, reversed_rotations)
|
104
110
|
end.join(' ')
|
105
111
|
new_tail_rotations = reversed_rotations.reverse! +
|
106
|
-
algorithm.moves[algorithm.length - num_tail_rotations
|
112
|
+
algorithm.moves[algorithm.length - num_tail_rotations..]
|
107
113
|
cancelled_rotations = Algorithm.new(new_tail_rotations).cancelled(3)
|
108
114
|
cancelled_rotations.empty? ? alg_string : "#{alg_string} #{cancelled_rotations}"
|
109
115
|
end
|
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twisty_puzzles
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
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
11
|
date: 2020-04-21 00:00:00.000000000 Z
|
@@ -112,16 +112,16 @@ dependencies:
|
|
112
112
|
name: rubocop
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '1.7'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '1.7'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: rubocop-performance
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -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,16 +253,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
253
253
|
requirements:
|
254
254
|
- - ">="
|
255
255
|
- !ruby/object:Gem::Version
|
256
|
-
version:
|
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
|
-
|
264
|
-
|
265
|
-
signing_key:
|
263
|
+
rubygems_version: 3.2.3
|
264
|
+
signing_key:
|
266
265
|
specification_version: 4
|
267
266
|
summary: Gem for my cube_trainer rails app. Some things are better left in a separate
|
268
267
|
gem with no rails, e.g. native extensions. The main purpose is to support my Rails
|