khetai 0.3.2 → 0.3.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 397d91c1cb38ed6a3c61ef8132415e88c74fcd80c933b124b028515109c656a1
4
- data.tar.gz: 24b234943d22a75d358a3c4caa5723469d6ee98665e710ecdcc981288b975f11
3
+ metadata.gz: 947bf0b2701da92a96a4fd24d29740cbfa460614f71cfe927754ba37840aab1d
4
+ data.tar.gz: 56a86968143c90e83cb1996a0aa86bf81e3e7f876aec8b077b361d35a7fab7fd
5
5
  SHA512:
6
- metadata.gz: 1bd6ceb6036f2a11bdce881f176ccccfe247d2884aa647b2da423b81cc3ff36120fa56fac338b4aacb55edcb2e11a4b722c85b3633c7c307e0419f0cf5b5bb07
7
- data.tar.gz: ed3131f4fefe5bc2050a396bb39036b73782bbabc39756445806dbf6b07485a4503e119fcef4baaa491033d3c695bfde451efac6a9ca83aa5287197b0af18d55
6
+ metadata.gz: c9bccf13dbbedce74f0a860a7fb0f09604caad0dc2fd395fe4fdd2484e7cd63f48c96ccaab076aeb1f0a6c75e3ca984dd85cf1973a8b348a949e8ba690c1af71
7
+ data.tar.gz: 7efc3618179af126593559ea32a7086f13e02c9ed8aaa509d01b8886f8eb12661e18ff0eb235754e609eacc161fb2a462a4e5162e877ba9b52c253f82104f6fb
data/.gitignore CHANGED
@@ -7,6 +7,7 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  /build/
10
+ /scripts/
10
11
  /ext/khetai/dev/build/
11
12
  /ext/khetai/dev/fltk-ui/build/
12
13
  /ext/khetai/dev/fltk-ui/build_khetai/
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.3.6
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- khetai (0.3.2)
4
+ khetai (0.3.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # KhetAI
2
2
 
3
+ An AI engine for the board game Khet. This gem provides move calculation for a specified color with configurable search depth and time limits.
4
+
3
5
  ## Installation
4
6
 
5
7
  $ gem install khetai
@@ -45,8 +47,19 @@ The representation of the board gets converted an array of 8-bit integers once i
45
47
 
46
48
  The internals of the gem are written in C, located in `ext/khetai`.
47
49
 
50
+ ## Development System Requirements
51
+
52
+ To build the gem from source, you'll need:
53
+ - Ruby >= 2.0.0
54
+ - GCC or compatible C compiler
55
+ - Development headers (usually provided by ruby-dev or ruby-devel package)
56
+
48
57
  ## Build and Deploy Commands
49
58
 
59
+ This project uses `asdf` as the version manager for Ruby. However, any Ruby version greater than `2.0.0` should work to compile this gem.
60
+
61
+ $ gem install bundler
62
+ $ bundle install
50
63
  $ bundle exec rake compile
51
64
  $ bundle exec rake build
52
65
  $ gem install pkg/khetai-<version>.gem
@@ -134,6 +134,11 @@ int GameBoard::handle(int event) {
134
134
  if (key == 'r') {
135
135
  resetPieces();
136
136
  } else if (key == FL_Enter) {
137
+ square_selected = false;
138
+ square_selected_num = -1;
139
+ redraw();
140
+ Fl::flush();
141
+
137
142
  const char *max_time_value = max_time_input->value();
138
143
  if (!max_time_value || max_time_value[0] == '\0') {
139
144
  max_time_input->value("5");
@@ -507,7 +512,6 @@ void GameBoard::updateLaserPosition() {
507
512
  if (laser_y >= y() + (rows * cell_height) || laser_y <= y() || laser_x >= x() + (cols * cell_width) || laser_x <= x()) {
508
513
  laser_active = false;
509
514
  laser_path.clear();
510
- redraw();
511
515
  return;
512
516
  }
513
517
 
@@ -65,7 +65,7 @@ class GameBoard : public Fl_Widget {
65
65
  int cell_width, cell_height;
66
66
  bool square_selected = false;
67
67
  bool laser_active = false;
68
- int laser_step = 3;
68
+ int laser_step = 5;
69
69
  LaserDirection laser_direction;
70
70
  float laser_y, laser_x;
71
71
  bool remove_piece = false;
@@ -154,7 +154,7 @@ int calculate_score() {
154
154
  enum Player player = j;
155
155
  for (int k = 0; k < 13; k++) {
156
156
  int i = get_board_index(player, k);
157
- if (i != EPT) {
157
+ if (i != EMPTY) {
158
158
  Square s = board[i];
159
159
  int value = 0;
160
160
  switch (get_piece(s)) {
@@ -217,15 +217,21 @@ void make_move(Move move) {
217
217
  // add starting piece to end location
218
218
  hash ^= keys[board[end]][end];
219
219
 
220
- enum Player moving_player = get_owner(moving_piece);
221
- update_piece_tracker(moving_player, start, end);
222
-
223
220
  // add ending piece to start location if swapping
224
221
  if (is_piece(board[start])) {
225
222
  hash ^= keys[board[start]][start];
226
223
 
227
224
  enum Player other_player = get_owner(board[start]);
228
- update_piece_tracker(other_player, end, start);
225
+ if (get_owner(moving_piece) == other_player)
226
+ update_piece_tracker(other_player, end, start, true);
227
+ else {
228
+ update_piece_tracker(moving_piece, start, end, false);
229
+ update_piece_tracker(other_player, end, start, false);
230
+ }
231
+
232
+ } else {
233
+ enum Player moving_player = get_owner(moving_piece);
234
+ update_piece_tracker(moving_player, start, end, false);
229
235
  }
230
236
 
231
237
  if (get_piece(moving_piece) == PHARAOH)
@@ -309,12 +315,18 @@ void undo_move() {
309
315
  board[start] = board[end];
310
316
  board[end] = moving_piece;
311
317
 
312
- enum Player moving_player = get_owner(moving_piece);
313
- update_piece_tracker(moving_player, start, end);
314
-
315
318
  if (board[start] != 0) {
316
319
  enum Player other_player = get_owner(board[start]);
317
- update_piece_tracker(other_player, end, start);
320
+
321
+ if (get_owner(moving_piece) == other_player)
322
+ update_piece_tracker(other_player, end, start, true);
323
+ else {
324
+ update_piece_tracker(moving_piece, start, end, false);
325
+ update_piece_tracker(other_player, end, start, false);
326
+ }
327
+ } else {
328
+ enum Player moving_player = get_owner(moving_piece);
329
+ update_piece_tracker(moving_player, start, end, false);
318
330
  }
319
331
 
320
332
  if (get_piece(moving_piece) == PHARAOH)
@@ -326,7 +338,7 @@ void undo_move() {
326
338
  void find_valid_moves(Move *valid_moves, int *vi) {
327
339
  for (int i = 0; i < 13; i++) {
328
340
  uint8_t board_pos = piece_trackers[whose_turn].positions[i];
329
- if (board_pos != EPT) {
341
+ if (board_pos != EMPTY) {
330
342
  enum Piece piece = get_piece(board[board_pos]);
331
343
  switch (piece) {
332
344
  case ANUBIS:
@@ -367,7 +379,7 @@ void find_valid_scarab_moves(int i, Move *valid_moves, int *vi) {
367
379
  for (int j = 0; j < 8; j++) {
368
380
  int dest = i + directions[j];
369
381
  if (can_move[whose_turn][dest]) {
370
- if (!is_piece(board[dest]) || (get_owner(board[dest]) != whose_turn && get_piece(board[dest]) != PHARAOH)) {
382
+ if (!is_piece(board[dest]) || (get_piece(board[dest]) != SCARAB && get_piece(board[dest]) != PHARAOH)) {
371
383
  valid_moves[(*vi)++] = new_move(i, dest, 0);
372
384
  }
373
385
  }
@@ -415,8 +427,8 @@ uint64_t get_board_hash() {
415
427
 
416
428
  void init_piece_trackers() {
417
429
  for (int i = 0; i < 13; i++) {
418
- piece_trackers[SILVER].positions[i] = EPT;
419
- piece_trackers[RED].positions[i] = EPT;
430
+ piece_trackers[SILVER].positions[i] = EMPTY;
431
+ piece_trackers[RED].positions[i] = EMPTY;
420
432
  }
421
433
 
422
434
  int si = 0;
@@ -435,8 +447,8 @@ void init_piece_trackers() {
435
447
  ri++;
436
448
  }
437
449
  } else {
438
- piece_trackers[SILVER].board_idx_position[i] = EPT;
439
- piece_trackers[RED].board_idx_position[i] = EPT;
450
+ piece_trackers[SILVER].board_idx_position[i] = EMPTY;
451
+ piece_trackers[RED].board_idx_position[i] = EMPTY;
440
452
  }
441
453
  }
442
454
  }
@@ -445,9 +457,9 @@ bool is_move_legal(Move move) {
445
457
  int start = get_start(move);
446
458
  int end = get_end(move);
447
459
  if (is_piece(board[start]) && get_owner(board[start]) == whose_turn) {
448
- if (!is_piece(board[end]) || get_rotation(move) != 0)
460
+ if (!is_piece(board[end]) || (get_rotation(move) != 0 && start == end))
449
461
  return true;
450
- else if (is_piece(board[end]) && get_piece(board[start]) == SCARAB && get_piece(board[end]) <= 3)
462
+ else if (is_piece(board[end]) && (get_piece(board[start]) == SCARAB && get_piece(board[end]) < 3))
451
463
  return true;
452
464
  }
453
465
  return false;
@@ -182,7 +182,10 @@ typedef struct HashEntry {
182
182
  extern HashEntry table[TABLE_SIZE];
183
183
  static inline HashEntry *search_table(uint64_t key) { return &table[key & TABLE_MASK]; };
184
184
 
185
- #define EPT 0xFF
185
+ #define NO_SWAP 0
186
+ #define SWAP_SAME 1
187
+ #define SWAP_OTHER 2
188
+ #define EMPTY 0xFF
186
189
  typedef struct PieceTracker {
187
190
  uint8_t positions[13];
188
191
  uint8_t board_idx_position[120];
@@ -191,20 +194,28 @@ extern PieceTracker piece_trackers[2];
191
194
 
192
195
  static inline uint8_t get_board_index(enum Player player, uint8_t pos_idx) { return piece_trackers[player].positions[pos_idx]; }
193
196
  static inline uint8_t get_position_index(enum Player player, uint8_t board_idx) { return piece_trackers[player].board_idx_position[board_idx]; }
194
- static inline void update_piece_tracker(enum Player player, uint8_t old_board_idx, uint8_t new_board_idx) {
197
+ static inline void update_piece_tracker(enum Player player, uint8_t old_board_idx, uint8_t new_board_idx, bool swap) {
195
198
  uint8_t pos_idx = get_position_index(player, old_board_idx);
196
- piece_trackers[player].positions[pos_idx] = new_board_idx;
197
- piece_trackers[player].board_idx_position[old_board_idx] = EPT;
198
- piece_trackers[player].board_idx_position[new_board_idx] = pos_idx;
199
+ if (!swap) {
200
+ piece_trackers[player].positions[pos_idx] = new_board_idx;
201
+ piece_trackers[player].board_idx_position[old_board_idx] = EMPTY;
202
+ piece_trackers[player].board_idx_position[new_board_idx] = pos_idx;
203
+ } else {
204
+ uint8_t other_pos_idx = get_position_index(player, new_board_idx);
205
+ piece_trackers[player].positions[pos_idx] = new_board_idx;
206
+ piece_trackers[player].positions[other_pos_idx] = old_board_idx;
207
+ piece_trackers[player].board_idx_position[new_board_idx] = pos_idx;
208
+ piece_trackers[player].board_idx_position[old_board_idx] = other_pos_idx;
209
+ }
199
210
  }
200
211
  static inline void remove_from_piece_tracker(enum Player player, uint8_t board_idx) {
201
212
  uint8_t pos_idx = get_position_index(player, board_idx);
202
- piece_trackers[player].positions[pos_idx] = EPT;
203
- piece_trackers[player].board_idx_position[board_idx] = EPT;
213
+ piece_trackers[player].positions[pos_idx] = EMPTY;
214
+ piece_trackers[player].board_idx_position[board_idx] = EMPTY;
204
215
  }
205
216
  static inline void add_to_piece_tracker(enum Player player, uint8_t board_idx) {
206
217
  uint8_t pos_idx = 0;
207
- while (piece_trackers[player].positions[pos_idx] != EPT)
218
+ while (piece_trackers[player].positions[pos_idx] != EMPTY)
208
219
  pos_idx++;
209
220
  piece_trackers[player].positions[pos_idx] = board_idx;
210
221
  piece_trackers[player].board_idx_position[board_idx] = pos_idx;
@@ -1,3 +1,3 @@
1
1
  module KhetAI
2
- VERSION = "0.3.2"
2
+ VERSION = "0.3.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: khetai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - jkugs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-12-07 00:00:00.000000000 Z
11
+ date: 2024-12-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -20,6 +20,7 @@ extra_rdoc_files: []
20
20
  files:
21
21
  - ".clang-format"
22
22
  - ".gitignore"
23
+ - ".tool-versions"
23
24
  - Gemfile
24
25
  - Gemfile.lock
25
26
  - LICENSE.txt
@@ -105,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
106
  - !ruby/object:Gem::Version
106
107
  version: '0'
107
108
  requirements: []
108
- rubygems_version: 3.4.10
109
+ rubygems_version: 3.5.22
109
110
  signing_key:
110
111
  specification_version: 4
111
112
  summary: Khet AI