khetai 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +13 -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 -257
- 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,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,8 +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
|
-
{
|
98
|
+
void get_row_col(int index, int &row, int &col) {
|
111
99
|
int border_width = 1;
|
112
100
|
int width_with_border = 10 + 2 * border_width;
|
113
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
|
}
|