khetai 0.2.3 → 0.3.2
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/.clang-format +14 -0
- data/Gemfile.lock +1 -1
- data/ext/khetai/dev/fltk-ui/ai_loader.cpp +20 -40
- data/ext/khetai/dev/fltk-ui/ai_loader.h +4 -5
- data/ext/khetai/dev/fltk-ui/game_board.cpp +98 -204
- data/ext/khetai/dev/fltk-ui/game_board.h +12 -19
- data/ext/khetai/dev/fltk-ui/game_board_util.cpp +16 -36
- data/ext/khetai/dev/fltk-ui/game_board_util.h +1 -1
- data/ext/khetai/dev/fltk-ui/khet.cpp +9 -14
- data/ext/khetai/dev/main.c +4 -6
- data/ext/khetai/khetai.c +25 -33
- data/ext/khetai/khetai_lib.c +252 -272
- data/ext/khetai/khetai_lib.h +88 -104
- data/lib/khetai/version.rb +1 -1
- metadata +3 -2
@@ -3,51 +3,45 @@
|
|
3
3
|
|
4
4
|
#include "ai_loader.h"
|
5
5
|
|
6
|
-
#include <FL/Fl_Widget.H>
|
7
|
-
#include <FL/Fl_PNG_Image.H>
|
8
6
|
#include <FL/Fl_Input.H>
|
7
|
+
#include <FL/Fl_PNG_Image.H>
|
8
|
+
#include <FL/Fl_Widget.H>
|
9
9
|
#include <string>
|
10
|
-
#include <vector>
|
11
10
|
#include <unordered_map>
|
11
|
+
#include <vector>
|
12
12
|
|
13
|
-
class GameBoard : public Fl_Widget
|
14
|
-
|
15
|
-
public:
|
13
|
+
class GameBoard : public Fl_Widget {
|
14
|
+
public:
|
16
15
|
GameBoard(int X, int Y, int W, int H, const char *L = 0);
|
17
16
|
Fl_Input *max_time_input, *max_depth_input;
|
18
17
|
void draw() override;
|
19
18
|
int handle(int event) override;
|
20
19
|
void init(const std::vector<std::vector<std::string>> &pieces);
|
21
20
|
|
22
|
-
enum Color
|
23
|
-
{
|
21
|
+
enum Color {
|
24
22
|
SILVER = 1,
|
25
23
|
RED = 2
|
26
24
|
};
|
27
|
-
enum LaserDirection
|
28
|
-
{
|
25
|
+
enum LaserDirection {
|
29
26
|
NORTH = 1,
|
30
27
|
EAST = 2,
|
31
28
|
SOUTH = 3,
|
32
29
|
WEST = 4
|
33
30
|
};
|
34
|
-
enum PieceType
|
35
|
-
{
|
31
|
+
enum PieceType {
|
36
32
|
ANUBIS,
|
37
33
|
PYRAMID,
|
38
34
|
SCARAB,
|
39
35
|
PHARAOH,
|
40
36
|
SPHINX
|
41
37
|
};
|
42
|
-
enum PieceOrientation
|
43
|
-
{
|
38
|
+
enum PieceOrientation {
|
44
39
|
ORIENT_NORTH,
|
45
40
|
ORIENT_EAST,
|
46
41
|
ORIENT_SOUTH,
|
47
42
|
ORIENT_WEST
|
48
43
|
};
|
49
|
-
enum ReflectionResult
|
50
|
-
{
|
44
|
+
enum ReflectionResult {
|
51
45
|
RESULT_DEAD,
|
52
46
|
RESULT_ABSORBED,
|
53
47
|
RESULT_EAST,
|
@@ -55,8 +49,7 @@ public:
|
|
55
49
|
RESULT_SOUTH,
|
56
50
|
RESULT_NORTH
|
57
51
|
};
|
58
|
-
enum MovePermission
|
59
|
-
{
|
52
|
+
enum MovePermission {
|
60
53
|
S = 1,
|
61
54
|
R = 2,
|
62
55
|
B = 3
|
@@ -66,7 +59,7 @@ public:
|
|
66
59
|
static std::pair<PieceType, PieceOrientation> getPieceTypeAndOrientation(const std::string &piece_str);
|
67
60
|
static void laser_timer_cb(void *data);
|
68
61
|
|
69
|
-
private:
|
62
|
+
private:
|
70
63
|
AILoader ai_loader;
|
71
64
|
int rows = 8, cols = 10, clicked_row = -1, clicked_col = -1, square_selected_num = -1;
|
72
65
|
int cell_width, cell_height;
|
@@ -1,28 +1,23 @@
|
|
1
1
|
#include "game_board_util.h"
|
2
2
|
|
3
|
-
#include <iostream>
|
4
3
|
#include <cstring>
|
4
|
+
#include <iostream>
|
5
5
|
|
6
|
-
std::vector<std::string> flatten_2d_vector_with_buffer(const std::vector<std::vector<std::string>> &vec2d)
|
7
|
-
{
|
6
|
+
std::vector<std::string> flatten_2d_vector_with_buffer(const std::vector<std::vector<std::string>> &vec2d) {
|
8
7
|
size_t new_rows = vec2d.size() + 2;
|
9
8
|
size_t new_cols = vec2d.empty() ? 0 : vec2d[0].size() + 2;
|
10
9
|
|
11
10
|
std::vector<std::vector<std::string>> buffered_vec2d(new_rows, std::vector<std::string>(new_cols, "--"));
|
12
11
|
|
13
|
-
for (size_t i = 0; i < vec2d.size(); ++i)
|
14
|
-
|
15
|
-
for (size_t j = 0; j < vec2d[i].size(); ++j)
|
16
|
-
{
|
12
|
+
for (size_t i = 0; i < vec2d.size(); ++i) {
|
13
|
+
for (size_t j = 0; j < vec2d[i].size(); ++j) {
|
17
14
|
buffered_vec2d[i + 1][j + 1] = vec2d[i][j];
|
18
15
|
}
|
19
16
|
}
|
20
17
|
|
21
18
|
std::vector<std::string> flattened;
|
22
|
-
for (const auto &row : buffered_vec2d)
|
23
|
-
|
24
|
-
for (const auto &elem : row)
|
25
|
-
{
|
19
|
+
for (const auto &row : buffered_vec2d) {
|
20
|
+
for (const auto &elem : row) {
|
26
21
|
flattened.push_back(elem);
|
27
22
|
}
|
28
23
|
}
|
@@ -30,11 +25,9 @@ std::vector<std::string> flatten_2d_vector_with_buffer(const std::vector<std::ve
|
|
30
25
|
return flattened;
|
31
26
|
}
|
32
27
|
|
33
|
-
char **vector_to_c_array(const std::vector<std::string> &vec)
|
34
|
-
{
|
28
|
+
char **vector_to_c_array(const std::vector<std::string> &vec) {
|
35
29
|
char **c_array = new char *[vec.size()];
|
36
|
-
for (size_t i = 0; i < vec.size(); ++i)
|
37
|
-
{
|
30
|
+
for (size_t i = 0; i < vec.size(); ++i) {
|
38
31
|
c_array[i] = new char[vec[i].size() + 1];
|
39
32
|
std::strcpy(c_array[i], vec[i].c_str());
|
40
33
|
}
|
@@ -42,17 +35,14 @@ char **vector_to_c_array(const std::vector<std::string> &vec)
|
|
42
35
|
return c_array;
|
43
36
|
}
|
44
37
|
|
45
|
-
void free_c_array(char **c_array, size_t size)
|
46
|
-
{
|
47
|
-
for (size_t i = 0; i < size; ++i)
|
48
|
-
{
|
38
|
+
void free_c_array(char **c_array, size_t size) {
|
39
|
+
for (size_t i = 0; i < size; ++i) {
|
49
40
|
delete[] c_array[i];
|
50
41
|
}
|
51
42
|
delete[] c_array;
|
52
43
|
}
|
53
44
|
|
54
|
-
Move call_ai_move(AILoader &ai_loader, const std::vector<std::vector<std::string>> &board_pieces, Player player, int max_depth, int max_time)
|
55
|
-
{
|
45
|
+
Move call_ai_move(AILoader &ai_loader, const std::vector<std::vector<std::string>> &board_pieces, Player player, int max_depth, int max_time) {
|
56
46
|
auto init_zobrist = ai_loader.get_init_zobrist();
|
57
47
|
auto reset_undo = ai_loader.get_reset_undo();
|
58
48
|
auto setup_board = ai_loader.get_setup_board();
|
@@ -80,8 +70,7 @@ Move call_ai_move(AILoader &ai_loader, const std::vector<std::vector<std::string
|
|
80
70
|
int depth = 1;
|
81
71
|
Move best_move = (Move)0;
|
82
72
|
Move current_move = (Move)0;
|
83
|
-
while (depth <= max_depth)
|
84
|
-
{
|
73
|
+
while (depth <= max_depth) {
|
85
74
|
printf("\nDEPTH: %-3d-> ", depth);
|
86
75
|
current_move = alphabeta_root(depth, player);
|
87
76
|
printf("MOVE -> START: %d, END: %d, ROTATION: %d\n", get_start(current_move), get_end(current_move), get_rotation(current_move));
|
@@ -106,16 +95,7 @@ Move call_ai_move(AILoader &ai_loader, const std::vector<std::vector<std::string
|
|
106
95
|
return best_move;
|
107
96
|
}
|
108
97
|
|
109
|
-
void get_row_col(int index, int &row, int &col)
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
int adjusted_index = index;
|
115
|
-
|
116
|
-
int row_with_border = adjusted_index / width_with_border;
|
117
|
-
int col_with_border = adjusted_index % width_with_border;
|
118
|
-
|
119
|
-
row = row_with_border - border_width;
|
120
|
-
col = col_with_border - border_width;
|
121
|
-
}
|
98
|
+
void get_row_col(int index, int &row, int &col) {
|
99
|
+
row = (index / 12) - 1;
|
100
|
+
col = (index % 12) - 1;
|
101
|
+
}
|
@@ -3,8 +3,8 @@
|
|
3
3
|
|
4
4
|
#include "ai_loader.h"
|
5
5
|
|
6
|
-
#include <vector>
|
7
6
|
#include <string>
|
7
|
+
#include <vector>
|
8
8
|
|
9
9
|
std::vector<std::string> flatten_2d_vector_with_buffer(const std::vector<std::vector<std::string>> &vec2d);
|
10
10
|
char **vector_to_c_array(const std::vector<std::string> &vec);
|
@@ -1,24 +1,20 @@
|
|
1
|
+
#include "game_board.h"
|
1
2
|
#include <FL/Fl.H>
|
3
|
+
#include <FL/Fl_Box.H>
|
2
4
|
#include <FL/Fl_Double_Window.H>
|
3
5
|
#include <FL/Fl_Input.H>
|
4
6
|
#include <FL/Fl_Int_Input.H>
|
5
|
-
#include <FL/Fl_Box.H>
|
6
|
-
#include "game_board.h"
|
7
7
|
|
8
|
-
class PositiveIntInput : public Fl_Int_Input
|
9
|
-
|
10
|
-
public:
|
8
|
+
class PositiveIntInput : public Fl_Int_Input {
|
9
|
+
public:
|
11
10
|
PositiveIntInput(int X, int Y, int W, int H, const char *L = 0)
|
12
11
|
: Fl_Int_Input(X, Y, W, H, L) {}
|
13
12
|
|
14
|
-
int handle(int event) override
|
15
|
-
{
|
13
|
+
int handle(int event) override {
|
16
14
|
int result = Fl_Int_Input::handle(event);
|
17
|
-
if (event == FL_KEYDOWN || event == FL_KEYUP || event == FL_PASTE)
|
18
|
-
{
|
15
|
+
if (event == FL_KEYDOWN || event == FL_KEYUP || event == FL_PASTE) {
|
19
16
|
const char *value = this->value();
|
20
|
-
if (value[0] == '-')
|
21
|
-
{
|
17
|
+
if (value[0] == '-') {
|
22
18
|
this->value("");
|
23
19
|
}
|
24
20
|
}
|
@@ -26,8 +22,7 @@ public:
|
|
26
22
|
}
|
27
23
|
};
|
28
24
|
|
29
|
-
int main(int argc, char **argv)
|
30
|
-
{
|
25
|
+
int main(int argc, char **argv) {
|
31
26
|
Fl_Double_Window *window = new Fl_Double_Window(800, 600, "Khet AI");
|
32
27
|
GameBoard *board = new GameBoard(50, 50, 700, 504);
|
33
28
|
|
@@ -50,7 +45,7 @@ int main(int argc, char **argv)
|
|
50
45
|
|
51
46
|
Fl_Box *max_depth_label = new Fl_Box(250, 10, 80, 30, "Max Depth:");
|
52
47
|
PositiveIntInput *max_depth_input = new PositiveIntInput(325, 15, 30, 20);
|
53
|
-
max_depth_input->value("
|
48
|
+
max_depth_input->value("25");
|
54
49
|
board->max_depth_input = max_depth_input;
|
55
50
|
|
56
51
|
window->end();
|
data/ext/khetai/dev/main.c
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
+
#include "../khetai_lib.h"
|
1
2
|
#include <stdio.h>
|
2
3
|
#include <stdlib.h>
|
3
4
|
#include <time.h>
|
4
|
-
#include "../khetai_lib.h"
|
5
5
|
|
6
6
|
char *init_board[120] =
|
7
7
|
{"--", "--", "--", "--", "--", "--", "--", "--", "--", "--", "--", "--",
|
@@ -15,8 +15,7 @@ char *init_board[120] =
|
|
15
15
|
"--", "--", "--", "p3", "a0", "x0", "a0", "--", "--", "--", "l0", "--",
|
16
16
|
"--", "--", "--", "--", "--", "--", "--", "--", "--", "--", "--", "--"};
|
17
17
|
|
18
|
-
int main()
|
19
|
-
{
|
18
|
+
int main() {
|
20
19
|
init_zobrist();
|
21
20
|
srand((unsigned)time(NULL));
|
22
21
|
|
@@ -31,9 +30,8 @@ int main()
|
|
31
30
|
int depth = 1;
|
32
31
|
Move best_move = (Move)0;
|
33
32
|
Move current_move = (Move)0;
|
34
|
-
while (depth <= max_depth)
|
35
|
-
|
36
|
-
current_move = alphabeta_root(depth, Red);
|
33
|
+
while (depth <= max_depth) {
|
34
|
+
current_move = alphabeta_root(depth, RED);
|
37
35
|
if (time(NULL) - start_time < max_time)
|
38
36
|
best_move = current_move;
|
39
37
|
else
|
data/ext/khetai/khetai.c
CHANGED
@@ -1,26 +1,29 @@
|
|
1
|
+
#include "khetai_lib.h"
|
1
2
|
#include <ruby.h>
|
2
3
|
#include <stdlib.h>
|
3
4
|
#include <time.h>
|
4
|
-
#include "khetai_lib.h"
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
static int convert_index(int i) {
|
7
|
+
int row = (i / 12) - 1;
|
8
|
+
int col = (i % 12) - 1;
|
9
|
+
|
10
|
+
return (row * 10) + col;
|
11
|
+
}
|
12
|
+
|
13
|
+
VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE _max_time) {
|
8
14
|
// verify parameters
|
9
15
|
int player = NUM2INT(_player);
|
10
|
-
if (player < 0 || player > 1)
|
11
|
-
{
|
16
|
+
if (player < 0 || player > 1) {
|
12
17
|
rb_raise(rb_eArgError, "player must be 0 (silver) or 1 (red)");
|
13
18
|
}
|
14
19
|
|
15
20
|
int max_time = NUM2INT(_max_time);
|
16
|
-
if (max_time < 1)
|
17
|
-
{
|
21
|
+
if (max_time < 1) {
|
18
22
|
rb_raise(rb_eArgError, "max_time (seconds) must be 1 or greater");
|
19
23
|
}
|
20
24
|
|
21
25
|
int max_depth = NUM2INT(_max_depth);
|
22
|
-
if (max_depth < 2 || max_depth > 25)
|
23
|
-
{
|
26
|
+
if (max_depth < 2 || max_depth > 25) {
|
24
27
|
rb_raise(rb_eArgError, "max_depth (ply) must be between 2 and 25");
|
25
28
|
}
|
26
29
|
|
@@ -29,33 +32,27 @@ VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE
|
|
29
32
|
unsigned int array_size = (unsigned int)RARRAY_LEN(board_array);
|
30
33
|
|
31
34
|
// verify board_array is valid
|
32
|
-
if (array_size != 80)
|
33
|
-
{
|
35
|
+
if (array_size != 80) {
|
34
36
|
rb_raise(rb_eArgError, "board_array must have exactly 80 elements");
|
35
37
|
}
|
36
38
|
const char valid_pieces[] = {'L', 'A', 'X', 'P', 'S', 'p', 'a', 'x', 's', 'l'};
|
37
39
|
size_t valid_pieces_count = 10;
|
38
|
-
for (unsigned int i = 0; i < array_size; i++)
|
39
|
-
{
|
40
|
+
for (unsigned int i = 0; i < array_size; i++) {
|
40
41
|
VALUE element = rb_ary_entry(board_array, i);
|
41
|
-
if (!RB_TYPE_P(element, T_STRING) || RSTRING_LEN(element) != 2)
|
42
|
-
{
|
42
|
+
if (!RB_TYPE_P(element, T_STRING) || RSTRING_LEN(element) != 2) {
|
43
43
|
rb_raise(rb_eArgError, "each element in board_array must be 2 characters");
|
44
44
|
}
|
45
45
|
|
46
46
|
// check if element in board_array is a valid string
|
47
47
|
const char *element_str = RSTRING_PTR(element);
|
48
48
|
int is_valid = 0;
|
49
|
-
for (unsigned int j = 0; j < valid_pieces_count; j++)
|
50
|
-
|
51
|
-
if ((element_str[0] == valid_pieces[j] && element_str[1] >= '0' && element_str[1] <= '3') || strcmp(element_str, "--") == 0)
|
52
|
-
{
|
49
|
+
for (unsigned int j = 0; j < valid_pieces_count; j++) {
|
50
|
+
if ((element_str[0] == valid_pieces[j] && element_str[1] >= '0' && element_str[1] <= '3') || strcmp(element_str, "--") == 0) {
|
53
51
|
is_valid = 1;
|
54
52
|
break;
|
55
53
|
}
|
56
54
|
}
|
57
|
-
if (!is_valid)
|
58
|
-
{
|
55
|
+
if (!is_valid) {
|
59
56
|
rb_raise(rb_eArgError, "each element in board_array must be a valid piece (example: 'p1') or empty ('--')");
|
60
57
|
}
|
61
58
|
}
|
@@ -64,22 +61,19 @@ VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE
|
|
64
61
|
char *init_board[120];
|
65
62
|
|
66
63
|
// top and bottom row padding
|
67
|
-
for (unsigned int i = 0; i < 12; i++)
|
68
|
-
{
|
64
|
+
for (unsigned int i = 0; i < 12; i++) {
|
69
65
|
init_board[i] = "--";
|
70
66
|
init_board[108 + i] = "--";
|
71
67
|
}
|
72
68
|
|
73
69
|
// left and right column padding
|
74
|
-
for (unsigned int i = 0; i < 8; i++)
|
75
|
-
{
|
70
|
+
for (unsigned int i = 0; i < 8; i++) {
|
76
71
|
init_board[12 * (i + 1)] = "--";
|
77
72
|
init_board[12 * (i + 2) - 1] = "--";
|
78
73
|
}
|
79
74
|
|
80
75
|
// fill in the rest of the board passed from ruby
|
81
|
-
for (unsigned int i = 0; i < array_size; i++)
|
82
|
-
{
|
76
|
+
for (unsigned int i = 0; i < array_size; i++) {
|
83
77
|
VALUE square = rb_ary_entry(board_array, i);
|
84
78
|
init_board[13 + ((i % 10) + ((i / 10) * 12))] = StringValueCStr(square);
|
85
79
|
}
|
@@ -95,8 +89,7 @@ VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE
|
|
95
89
|
int depth = 1;
|
96
90
|
Move best_move = (Move)0;
|
97
91
|
Move current_move = (Move)0;
|
98
|
-
while ((time(NULL) - start_time < max_time) && (depth <= max_depth))
|
99
|
-
{
|
92
|
+
while ((time(NULL) - start_time < max_time) && (depth <= max_depth)) {
|
100
93
|
best_move = current_move;
|
101
94
|
current_move = alphabeta_root(depth, player);
|
102
95
|
depth++;
|
@@ -104,15 +97,14 @@ VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE
|
|
104
97
|
make_move(best_move);
|
105
98
|
|
106
99
|
VALUE out = rb_ary_new2(3);
|
107
|
-
rb_ary_store(out, 0, INT2NUM(get_start(best_move)));
|
108
|
-
rb_ary_store(out, 1, INT2NUM(get_end(best_move)));
|
100
|
+
rb_ary_store(out, 0, INT2NUM(convert_index(get_start(best_move))));
|
101
|
+
rb_ary_store(out, 1, INT2NUM(convert_index(get_end(best_move))));
|
109
102
|
rb_ary_store(out, 2, INT2NUM(get_rotation(best_move)));
|
110
103
|
|
111
104
|
return out;
|
112
105
|
}
|
113
106
|
|
114
|
-
void Init_khetai()
|
115
|
-
{
|
107
|
+
void Init_khetai() {
|
116
108
|
VALUE KhetAI = rb_define_module("KhetAI");
|
117
109
|
rb_define_singleton_method(KhetAI, "move", move, 4);
|
118
110
|
}
|