khetai 0.2.2 → 0.3.0
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/README.md +5 -2
- data/ext/khetai/dev/fltk-ui/ai_loader.cpp +23 -38
- data/ext/khetai/dev/fltk-ui/ai_loader.h +5 -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 +15 -25
- 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 +16 -31
- data/ext/khetai/khetai_lib.c +220 -256
- data/ext/khetai/khetai_lib.h +88 -67
- 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,18 +35,16 @@ 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();
|
47
|
+
auto reset_undo = ai_loader.get_reset_undo();
|
57
48
|
auto setup_board = ai_loader.get_setup_board();
|
58
49
|
auto print_board = ai_loader.get_print_board();
|
59
50
|
auto set_time_parameters = ai_loader.get_set_time_parameters();
|
@@ -65,6 +56,7 @@ Move call_ai_move(AILoader &ai_loader, const std::vector<std::vector<std::string
|
|
65
56
|
|
66
57
|
char **c_board = vector_to_c_array(flatten_2d_vector_with_buffer(board_pieces));
|
67
58
|
|
59
|
+
reset_undo();
|
68
60
|
init_zobrist();
|
69
61
|
srand((unsigned)time(NULL));
|
70
62
|
|
@@ -78,8 +70,7 @@ Move call_ai_move(AILoader &ai_loader, const std::vector<std::vector<std::string
|
|
78
70
|
int depth = 1;
|
79
71
|
Move best_move = (Move)0;
|
80
72
|
Move current_move = (Move)0;
|
81
|
-
while (depth <= max_depth)
|
82
|
-
{
|
73
|
+
while (depth <= max_depth) {
|
83
74
|
printf("\nDEPTH: %-3d-> ", depth);
|
84
75
|
current_move = alphabeta_root(depth, player);
|
85
76
|
printf("MOVE -> START: %d, END: %d, ROTATION: %d\n", get_start(current_move), get_end(current_move), get_rotation(current_move));
|
@@ -104,8 +95,7 @@ Move call_ai_move(AILoader &ai_loader, const std::vector<std::vector<std::string
|
|
104
95
|
return best_move;
|
105
96
|
}
|
106
97
|
|
107
|
-
void get_row_col(int index, int &row, int &col)
|
108
|
-
{
|
98
|
+
void get_row_col(int index, int &row, int &col) {
|
109
99
|
int border_width = 1;
|
110
100
|
int width_with_border = 10 + 2 * border_width;
|
111
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,22 @@
|
|
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
|
-
VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE _max_time)
|
7
|
-
{
|
6
|
+
VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE _max_time) {
|
8
7
|
// verify parameters
|
9
8
|
int player = NUM2INT(_player);
|
10
|
-
if (player < 0 || player > 1)
|
11
|
-
{
|
9
|
+
if (player < 0 || player > 1) {
|
12
10
|
rb_raise(rb_eArgError, "player must be 0 (silver) or 1 (red)");
|
13
11
|
}
|
14
12
|
|
15
13
|
int max_time = NUM2INT(_max_time);
|
16
|
-
if (max_time < 1)
|
17
|
-
{
|
14
|
+
if (max_time < 1) {
|
18
15
|
rb_raise(rb_eArgError, "max_time (seconds) must be 1 or greater");
|
19
16
|
}
|
20
17
|
|
21
18
|
int max_depth = NUM2INT(_max_depth);
|
22
|
-
if (max_depth < 2 || max_depth > 25)
|
23
|
-
{
|
19
|
+
if (max_depth < 2 || max_depth > 25) {
|
24
20
|
rb_raise(rb_eArgError, "max_depth (ply) must be between 2 and 25");
|
25
21
|
}
|
26
22
|
|
@@ -29,33 +25,27 @@ VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE
|
|
29
25
|
unsigned int array_size = (unsigned int)RARRAY_LEN(board_array);
|
30
26
|
|
31
27
|
// verify board_array is valid
|
32
|
-
if (array_size != 80)
|
33
|
-
{
|
28
|
+
if (array_size != 80) {
|
34
29
|
rb_raise(rb_eArgError, "board_array must have exactly 80 elements");
|
35
30
|
}
|
36
31
|
const char valid_pieces[] = {'L', 'A', 'X', 'P', 'S', 'p', 'a', 'x', 's', 'l'};
|
37
32
|
size_t valid_pieces_count = 10;
|
38
|
-
for (unsigned int i = 0; i < array_size; i++)
|
39
|
-
{
|
33
|
+
for (unsigned int i = 0; i < array_size; i++) {
|
40
34
|
VALUE element = rb_ary_entry(board_array, i);
|
41
|
-
if (!RB_TYPE_P(element, T_STRING) || RSTRING_LEN(element) != 2)
|
42
|
-
{
|
35
|
+
if (!RB_TYPE_P(element, T_STRING) || RSTRING_LEN(element) != 2) {
|
43
36
|
rb_raise(rb_eArgError, "each element in board_array must be 2 characters");
|
44
37
|
}
|
45
38
|
|
46
39
|
// check if element in board_array is a valid string
|
47
40
|
const char *element_str = RSTRING_PTR(element);
|
48
41
|
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
|
-
{
|
42
|
+
for (unsigned int j = 0; j < valid_pieces_count; j++) {
|
43
|
+
if ((element_str[0] == valid_pieces[j] && element_str[1] >= '0' && element_str[1] <= '3') || strcmp(element_str, "--") == 0) {
|
53
44
|
is_valid = 1;
|
54
45
|
break;
|
55
46
|
}
|
56
47
|
}
|
57
|
-
if (!is_valid)
|
58
|
-
{
|
48
|
+
if (!is_valid) {
|
59
49
|
rb_raise(rb_eArgError, "each element in board_array must be a valid piece (example: 'p1') or empty ('--')");
|
60
50
|
}
|
61
51
|
}
|
@@ -64,22 +54,19 @@ VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE
|
|
64
54
|
char *init_board[120];
|
65
55
|
|
66
56
|
// top and bottom row padding
|
67
|
-
for (unsigned int i = 0; i < 12; i++)
|
68
|
-
{
|
57
|
+
for (unsigned int i = 0; i < 12; i++) {
|
69
58
|
init_board[i] = "--";
|
70
59
|
init_board[108 + i] = "--";
|
71
60
|
}
|
72
61
|
|
73
62
|
// left and right column padding
|
74
|
-
for (unsigned int i = 0; i < 8; i++)
|
75
|
-
{
|
63
|
+
for (unsigned int i = 0; i < 8; i++) {
|
76
64
|
init_board[12 * (i + 1)] = "--";
|
77
65
|
init_board[12 * (i + 2) - 1] = "--";
|
78
66
|
}
|
79
67
|
|
80
68
|
// fill in the rest of the board passed from ruby
|
81
|
-
for (unsigned int i = 0; i < array_size; i++)
|
82
|
-
{
|
69
|
+
for (unsigned int i = 0; i < array_size; i++) {
|
83
70
|
VALUE square = rb_ary_entry(board_array, i);
|
84
71
|
init_board[13 + ((i % 10) + ((i / 10) * 12))] = StringValueCStr(square);
|
85
72
|
}
|
@@ -95,8 +82,7 @@ VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE
|
|
95
82
|
int depth = 1;
|
96
83
|
Move best_move = (Move)0;
|
97
84
|
Move current_move = (Move)0;
|
98
|
-
while ((time(NULL) - start_time < max_time) && (depth <= max_depth))
|
99
|
-
{
|
85
|
+
while ((time(NULL) - start_time < max_time) && (depth <= max_depth)) {
|
100
86
|
best_move = current_move;
|
101
87
|
current_move = alphabeta_root(depth, player);
|
102
88
|
depth++;
|
@@ -111,8 +97,7 @@ VALUE move(VALUE self, VALUE board_array, VALUE _player, VALUE _max_depth, VALUE
|
|
111
97
|
return out;
|
112
98
|
}
|
113
99
|
|
114
|
-
void Init_khetai()
|
115
|
-
{
|
100
|
+
void Init_khetai() {
|
116
101
|
VALUE KhetAI = rb_define_module("KhetAI");
|
117
102
|
rb_define_singleton_method(KhetAI, "move", move, 4);
|
118
103
|
}
|