shurikenengine 0.31

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1567b25d621478c1c3b9625b0d7ae5838e869b8d8e5fc4027354f512741d7101
4
+ data.tar.gz: f0760bfb2e51e5b882152e830f9a0ab45e3b62a49785b71e4ee4d3e215fd50f8
5
+ SHA512:
6
+ metadata.gz: becdc3f1bf04b553988adce2c88c82a501b23289579a0aefe40616707c57be207ff600647e3972150cfa0387a3d46bb26b9fb9a08a23f8305786961d86a3fbdf
7
+ data.tar.gz: 3597b8407a4fbb60b2334225ab4f035119428db37c11c5709cca89bbe4222286a2cb29c4a5198313d4631268bf89a18428ea5300a5b4a6207232cf75ca0c0676
data/bin/shuriken ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'shuriken'
4
+
5
+ Shuriken.go
data/lib/shuriken.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative "./shuriken/shuriken"
@@ -0,0 +1,118 @@
1
+ ##
2
+ # Shuriken, a Ruby chess variant engine
3
+ # Author: Toni Helminen
4
+ # License: GPLv3
5
+ ##
6
+
7
+ module Shuriken
8
+
9
+ require 'benchmark'
10
+
11
+ module Bench
12
+ def Bench.f1
13
+ n = 0
14
+ 1000_000.times { |i| n += (i%80) }
15
+ n
16
+ end
17
+
18
+ def Bench.f2
19
+ n = 0
20
+ 1000_000.times { |i| n += (i%80) }
21
+ n
22
+ end
23
+
24
+ def Bench.f3
25
+ n, i = 0, 0
26
+ while i < 1000_000
27
+ n, i = n + (i%80), i + 1
28
+ end
29
+ n
30
+ end
31
+
32
+ def Bench.f4
33
+ n = 0
34
+ 1000_000.times { |i| n += i%42 }
35
+ n
36
+ end
37
+
38
+ def Bench.f5
39
+ n = 0
40
+ 1000_000.times { |i| n += i.modulo 42 }
41
+ n
42
+ end
43
+
44
+ def Bench.f6
45
+ s, caps = ["abc", "def", "ghi", "jkl"], ""
46
+ 100_000.times { caps = s.map { |str| str.upcase } }
47
+ caps
48
+ end
49
+
50
+ def Bench.f7
51
+ s, caps = ["abc", "def", "ghi", "jkl"], ""
52
+ 100_000.times { caps = s.map(&:upcase) }
53
+ caps
54
+ end
55
+
56
+ def Bench.f8
57
+ s, s2 = "a", "abc"
58
+ 20_000.times { s += s2 }
59
+ end
60
+
61
+ def Bench.f9
62
+ s, s2 = "a", "abc"
63
+ 20_000.times { s << s2 }
64
+ end
65
+
66
+ def Bench.f10
67
+ s, s2 = "a", "abc"
68
+ 20_000.times { s = "#{s}#{s2}" }
69
+ end
70
+
71
+ def Bench.header(msg)
72
+ puts "... #{msg} ..."
73
+ end
74
+
75
+ def Bench.loops
76
+ header("loops")
77
+ Benchmark.bm(10) do |x|
78
+ x.report("each") { f1 }
79
+ x.report("times") { f2 }
80
+ x.report("while") { f3 }
81
+ #x.compare!
82
+ end
83
+ end
84
+
85
+ def Bench.modulo
86
+ header("modulo")
87
+ Benchmark.bm(10) do |x|
88
+ x.report("%") { f4 }
89
+ x.report("modulo") { f5 }
90
+ end
91
+ end
92
+
93
+ def Bench.caps
94
+ header("caps")
95
+ Benchmark.bm(10) do |x|
96
+ x.report(".upcase") { f6 }
97
+ x.report("&:upcase") { f7 }
98
+ end
99
+ end
100
+
101
+ def Bench.strings
102
+ header("strings")
103
+ Benchmark.bm(10) do |x|
104
+ x.report("+=") { f8 }
105
+ x.report("<<") { f9 }
106
+ x.report("\#\{\}") { f10 }
107
+ end
108
+ end
109
+
110
+ def Bench.go
111
+ loops
112
+ modulo
113
+ caps
114
+ strings
115
+ end
116
+ end # module Bench
117
+
118
+ end # module Shuriken
@@ -0,0 +1,70 @@
1
+ ##
2
+ # Shuriken, a Ruby chess variant engine
3
+ # Author: Toni Helminen
4
+ # License: GPLv3
5
+ ##
6
+
7
+ module Shuriken
8
+
9
+ class Board
10
+ def brd2str
11
+ s, empty, counter = "", 0, 0
12
+ 80.times do |j|
13
+ i = 10 * (7 - j / 10) + ( j % 10 )
14
+ p = @brd[i]
15
+ if p != 0
16
+ if empty > 0
17
+ s += empty.to_s
18
+ empty = 0
19
+ end
20
+ s += "fcakqrbnp.PNBRQKACF"[p + 9]
21
+ else
22
+ empty += 1
23
+ end
24
+ counter += 1
25
+ if counter % 10 == 0
26
+ s += empty.to_s if empty > 0
27
+ s += "/" if counter < 80
28
+ empty = 0
29
+ end
30
+ end
31
+ s
32
+ end
33
+
34
+ def wtm2str
35
+ @wtm ? "w" : "b"
36
+ end
37
+
38
+ def castle2str
39
+ return "-" if @castle == 0
40
+ s = ""
41
+ if @variant == "cabarandom"
42
+ a = "ABCDEFGHIJ"
43
+ s += a[@castle_squares[1]] if @castle & 0x1 == 0x1
44
+ s += a[@castle_squares[5]] if @castle & 0x2 == 0x2
45
+ s += a[@castle_squares[1]].downcase if @castle & 0x4 == 0x4
46
+ s += a[@castle_squares[5]].downcase if @castle & 0x8 == 0x8
47
+ else
48
+ s += @castle & 0x1 == 0x1 ? "K" : ""
49
+ s += @castle & 0x2 == 0x2 ? "Q" : ""
50
+ s += @castle & 0x4 == 0x4 ? "k" : ""
51
+ s += @castle & 0x8 == 0x8 ? "q" : ""
52
+ end
53
+ s
54
+ end
55
+
56
+ def ep2str
57
+ return "-" if @ep == -1
58
+ "abcdefghijkl"[ @ep % 10 ] + (@ep / 10).to_s
59
+ end
60
+
61
+ def r502str
62
+ @r50.to_s
63
+ end
64
+
65
+ def tofen
66
+ "#{brd2str} #{wtm2str} #{castle2str} #{ep2str} #{r502str}"
67
+ end
68
+ end # class Board
69
+
70
+ end # module Shuriken
@@ -0,0 +1,422 @@
1
+ ##
2
+ # Shuriken, a Ruby chess variant engine
3
+ # Author: Toni Helminen
4
+ # License: GPLv3
5
+ ##
6
+
7
+ module Shuriken
8
+
9
+ class BoardCaparandom < Shuriken::Board
10
+ GOTHIC_POS = "rnbqckabnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBQCKABNR w KQkq - 0 1"
11
+ CAPA_POS = "rnabqkbcnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNABQKBCNR w KQkq - 0 1"
12
+ FALCON_POS = "rnbfqkfbnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBFQKFBNR w KQkq - 0 1"
13
+
14
+ PIECES = {
15
+ ".": 0,
16
+ "P": 1, # Pawn
17
+ "p": -1,
18
+ "N": 2, # Knight
19
+ "n": -2,
20
+ "B": 3, # Bishop
21
+ "b": -3,
22
+ "R": 4, # Rook
23
+ "r": -4,
24
+ "Q": 5, # Queen
25
+ "q": -5,
26
+ "K": 6, # King
27
+ "k": -6,
28
+ "A": 7, # Arcbishop
29
+ "a": -7,
30
+ "C": 8, # Chancellor
31
+ "c": -8,
32
+ "F": 9, # Falcon
33
+ "f": -9
34
+ }.freeze
35
+
36
+ attr_accessor :brd, :variant, :nodetype, :hash, :ep, :wtm, :eat, :from, :to, :castle, :castle_squares, :r50, :score, :promo, :castled, :index
37
+
38
+ def initialize(variant)
39
+ @variant = variant
40
+ initme
41
+ end
42
+
43
+ def initme
44
+ @brd = [0] * 80
45
+ @castle, @ep, @wtm, @from, @to, @r50, @eat = 0, -1, true, 0, 0, 0, 0
46
+ @score, @promo, @castled = 0, 0, 0
47
+ # white O-O : [ king_pos, rook_pos, castle_square, direction ]
48
+ # white O-O-O : [ king_pos, rook_pos, castle_square, direction ]
49
+ @castle_squares = [-1] * 2 * 4
50
+ @index = 0
51
+ @hash = 0
52
+ @nodetype = 0 # 2 draw 1 win -1 loss
53
+ end
54
+
55
+ def mgen_generator
56
+ @wtm ? Shuriken::MgenCaparandomWhite.new(self) : Shuriken::MgenCaparandomBlack.new(self)
57
+ end
58
+
59
+ def create_hash
60
+ @hash = 0
61
+ 80.times do | i |
62
+ @hash ^= Shuriken::Zobrist.get(20 * i + 8 + @brd[i])
63
+ end
64
+ @hash ^= Shuriken::Zobrist.get(20 * 80 + (@wtm ? 1 : 0))
65
+ @hash ^= Shuriken::Zobrist.get(20 * 81 + (@ep == -1 ? 1 : 0))
66
+ @hash ^= Shuriken::Zobrist.get(20 * 82 + @castle)
67
+ end
68
+
69
+ # TODO write castling stuff
70
+ def legal?
71
+ pieces = [0] * 20
72
+ @brd.each { |p| pieces[p + 9] += 1 }
73
+ return false if pieces[-6 + 9] == 0 || pieces[6 + 9] == 0
74
+ true
75
+ end
76
+
77
+ def move_str
78
+ return "O-O" if (@castled == 1 && @variant == "caparandom")
79
+ return "O-O-O" if (@castled == 2 && @variant == "caparandom")
80
+ fromx, fromy = x_coord(@from), y_coord(@from)
81
+ tox, toy = x_coord(@to), y_coord(@to)
82
+ s = ("a".ord + fromx).chr
83
+ s << (fromy + 1).to_s
84
+ s << ("a".ord + tox).chr
85
+ s << (toy + 1).to_s
86
+ ps = @variant == "falcon" ? "nbrqkkkf" : "nbrqkac"
87
+ if @promo > 1
88
+ s << ps[@promo - 2]
89
+ elsif @promo < -1
90
+ s << ps[-@promo - 2]
91
+ end
92
+ s
93
+ end
94
+
95
+ def make_move(me, from, to)
96
+ fail unless (good_coord?(from) && good_coord?(to))
97
+ @eat = @brd[to]
98
+ @ep = -1
99
+ @r50 += 1
100
+ @brd[to] = me
101
+ @brd[from] = 0
102
+ @r50 = 0 if @eat
103
+ if @wtm
104
+ if me == 1
105
+ @r50 = 0
106
+ @ep = from + 10 if (y_coord(from) == 1 && y_coord(to) == 3)
107
+ elsif me == 6
108
+ @castle &= 0x4 | 0x8
109
+ end
110
+ else
111
+ if me == -1
112
+ @r50 = 0
113
+ @ep = from - 10 if (y_coord(from) == 8 - 2 && y_coord(to) == 8 - 4)
114
+ elsif me == -6
115
+ @castle &= 0x1 | 0x2
116
+ end
117
+ end
118
+ handle_castle_rights
119
+ end
120
+
121
+ def find_white_king
122
+ #80.times do |i|
123
+ # return i if @brd[i] == 6
124
+ #end
125
+ #fail
126
+ @brd.index { | x | x == 6 }
127
+ end
128
+
129
+ def find_black_king
130
+ #80.times do |i|
131
+ # return i if @brd[i] == -6
132
+ #end
133
+ #fail
134
+ @brd.index { | x | x == -6 }
135
+ end
136
+
137
+ def find_piece_all(piece)
138
+ @brd.index { | x | x == piece }
139
+ end
140
+
141
+ # scans ->
142
+ def find_piece(start_square, end_square, me, diff = 1)
143
+ i = start_square
144
+ loop do
145
+ return i if @brd[i] == me
146
+ fail "Shuriken Error: Couldn't Find: '#{me}'" if i == end_square
147
+ i += diff
148
+ end
149
+ end
150
+
151
+ # scans ->
152
+ def just_kings?
153
+ 80.times do |i|
154
+ return false if (@brd[i] != 6 && @brd[i] != -6)
155
+ end
156
+ true
157
+ end
158
+
159
+ def material_draw?
160
+ 80.times do |i|
161
+ return false if (@brd[i] != 6 && @brd[i] != -6 && @brd[i] != 0)
162
+ end
163
+ true
164
+ end
165
+
166
+ def handle_castle_rights
167
+ if @castle & 0x1 == 0x1
168
+ @castle &= (0x2 | 0x4 | 0x8) if @brd[@castle_squares[1]] != 4
169
+ end
170
+ if @castle & 0x2 == 0x2
171
+ @castle &= (0x1 | 0x4 | 0x8) if @brd[@castle_squares[1 + 4]] != 4
172
+ end
173
+ if @castle & 0x4 == 0x4
174
+ @castle &= (0x1 | 0x2 | 0x8) if @brd[70 + @castle_squares[1]] != -4
175
+ end
176
+ if @castle & 0x8 == 0x8
177
+ @castle &= (0x1 | 0x2 | 0x4) if @brd[70 + @castle_squares[1 + 4]] != -4
178
+ end
179
+ end
180
+
181
+ def make_castle_squares
182
+ if @castle & 0x1 == 0x1
183
+ king = find_piece(0, 10 - 1, 6, 1)
184
+ rook_r = find_piece(king, 10 - 1, 4, 1)
185
+ castle_square = 10 - 2
186
+ @castle_squares[0] = king
187
+ @castle_squares[1] = rook_r
188
+ @castle_squares[2] = castle_square
189
+ @castle_squares[3] = king < castle_square ? 1 : -1
190
+ end
191
+ if @castle & 0x2 == 0x2
192
+ king = find_piece(0, 10 - 1, 6, 1)
193
+ rook_l = find_piece(king, 0, 4, -1)
194
+ castle_square = 2
195
+ @castle_squares[4] = king
196
+ @castle_squares[5] = rook_l
197
+ @castle_squares[6] = castle_square
198
+ @castle_squares[7] = king < castle_square ? 1 : -1
199
+ end
200
+ if @castle & 0x4 == 0x4
201
+ king = find_piece(10 * 8 - 10, 10 * 8 - 1, -6, 1)
202
+ rook_r = find_piece(king, 10 * 8 - 1, -4, 1)
203
+ castle_square = 10 * 8 - 2
204
+ pos = 10 * 8 - 10
205
+ @castle_squares[0] = king - pos
206
+ @castle_squares[1] = rook_r - pos
207
+ @castle_squares[2] = castle_square - pos
208
+ @castle_squares[3] = king < castle_square ? 1 : -1
209
+ end
210
+ if @castle & 0x8 == 0x8
211
+ king = find_piece(10 * 8 - 10, 10 * 8 - 1, -6, 1)
212
+ rook_l = find_piece(king, 10 * 8 - 10, -4, -1)
213
+ castle_square = 10 * 8 - 10 + 2
214
+ pos = 10 * 8 - 10
215
+ @castle_squares[4] = king - pos
216
+ @castle_squares[5] = rook_l - pos
217
+ @castle_squares[6] = castle_square - pos
218
+ @castle_squares[7] = king < castle_square ? 1 : -1
219
+ end
220
+ end
221
+
222
+ def copy_me()
223
+ copy = Shuriken::BoardCaparandom.new(@variant)
224
+ copy.brd = @brd.dup
225
+ copy.castle_squares = @castle_squares.dup
226
+ copy.castle = @castle
227
+ copy.ep = @ep
228
+ copy.wtm = @wtm
229
+ copy.from = @from
230
+ copy.to = @to
231
+ copy
232
+ end
233
+
234
+ def startpos(spos)
235
+ pos = case spos
236
+ when "gothic"
237
+ GOTHIC_POS
238
+ when "capablanca"
239
+ CAPA_POS
240
+ when "falcon"
241
+ FALCON_POS
242
+ else
243
+ CAPA_POS
244
+ end
245
+ use_fen(pos)
246
+ end
247
+
248
+ def use_fen(pos)
249
+ initme
250
+ fen(pos)
251
+ make_castle_squares
252
+ end
253
+
254
+ def y_coord(n)
255
+ n / 10
256
+ end
257
+
258
+ def x_coord(n)
259
+ n % 10
260
+ end
261
+
262
+ def last_rank?(square)
263
+ y_coord(square) == 7 ? true : false
264
+ end
265
+
266
+ def first_rank?(x)
267
+ y_coord(x) == 0 ? true : false
268
+ end
269
+
270
+ def empty?(i)
271
+ @brd[i] == 0 ? true : false
272
+ end
273
+
274
+ def walkable_w?(square)
275
+ @brd[square] < 1 ? true : false
276
+ end
277
+
278
+ def walkable_b?(square)
279
+ @brd[square] > -1 ? true : false
280
+ end
281
+
282
+ def black?(square)
283
+ @brd[square] < 0 ? true : false
284
+ end
285
+
286
+ def white?(square)
287
+ @brd[square] > 0 ? true : false
288
+ end
289
+
290
+ def is_on_board?(x, y)
291
+ (x >= 0 && x < 10 && y >= 0 && y < 8) ? true : false
292
+ end
293
+
294
+ def good_coord?(i)
295
+ (i >= 0 && i < 80) ? true : false
296
+ end
297
+
298
+ def mirror_board
299
+ half = ((10 * 8) / 2 - 1).to_i
300
+ (0..half).each do | i |
301
+ x, y = x_coord(i), y_coord(i)
302
+ flip_y = x + (8 - 1 - y) * 10
303
+ p1 = @brd[i]
304
+ p2 = @brd[flip_y]
305
+ @brd[i] = p2
306
+ @brd[flip_y] = p1
307
+ end
308
+ end
309
+
310
+ def flip_coord(coord)
311
+ (8 - 1 - y_coord(coord)) * 10 + x_coord(coord)
312
+ end
313
+
314
+ def fen_board(s)
315
+ i = 0
316
+ s.gsub(/\d+/) { | m | "_" * m.to_i }
317
+ .gsub(/\//) { | m | "" }
318
+ .each_char do | c |
319
+ PIECES.each do | pie, num |
320
+ if c == pie.to_s
321
+ @brd[i] = num
322
+ break
323
+ end
324
+ end
325
+ i += 1
326
+ end
327
+ end
328
+
329
+ def fen_wtm(s)
330
+ @wtm = s == "w" ? true : false
331
+ end
332
+
333
+ def fen_KQkq(s)
334
+ found = false
335
+ s.each_char do | c |
336
+ {0x1 => "K", 0x2 => "Q", 0x4 => "k", 0x8 => "q"}.each do | a, b |
337
+ if c == b
338
+ @castle |= a
339
+ found = true
340
+ end
341
+ end
342
+ end
343
+ return if found
344
+ # caparandom castling
345
+ # setboard 3rkcnrbb/pppn1ppppp/3p6/4p5/7P1P/6CB2/PPPPPPPP1B/RNAQK2R2 w HAh - 1 8
346
+ wking, bking = find_piece_all(6) - 70, find_piece_all(-6)
347
+ s.each_char do | c |
348
+ if ("A".."J").include? c
349
+ num = c.ord - "A".ord
350
+ @castle |= num > wking ? 0x1 : 0x2
351
+ elsif ("a".."j").include? c
352
+ num = c.ord - "a".ord
353
+ @castle |= num > bking ? 0x4 : 0x8
354
+ end
355
+ end
356
+ end
357
+
358
+ def fen_ep(s)
359
+ return if (s == "-" or s.length < 2)
360
+ @ep = (s[0].ord - "a".ord) + 10 * s[1].to_i
361
+ end
362
+
363
+ def fen_r50(s)
364
+ @r50 = s.to_i
365
+ end
366
+
367
+ def fen(str)
368
+ initme
369
+ s = str.strip.split(" ")
370
+ fen_board(s[0]) if s.length >= 0
371
+ fen_wtm(s[1]) if s.length >= 1
372
+ fen_KQkq(s[2]) if s.length >= 2
373
+ fen_ep(s[3]) if s.length >= 3
374
+ fen_r50(s[4]) if s.length >= 4
375
+ mirror_board
376
+ end
377
+
378
+ def str_castle
379
+ s = ""
380
+ {"K" => 0x1, "Q" => 0x2, "k" => 0x4, "q" => 0x8}.each do |a, b|
381
+ s += a if @castle.to_i & b == b
382
+ end
383
+ s.empty? ? "-" : s
384
+ end
385
+
386
+ def eval
387
+ Shuriken::EvalCaparandom.eval(self)
388
+ end
389
+
390
+ def material
391
+ Shuriken::EvalCaparandom.material(self)
392
+ end
393
+
394
+ def print_board
395
+ s =""
396
+ flip_it = false
397
+ 80.times do | i |
398
+ x, y = x_coord(i), y_coord(i)
399
+ p = @brd[x + (8 - y - 1) * 10]
400
+ if flip_it
401
+ p = -@brd[x + y * 10]
402
+ end
403
+ ch = "."
404
+ PIECES.each do |pie, num|
405
+ if num.to_s == p.to_s
406
+ ch = pie.to_s
407
+ end
408
+ end
409
+ s << ch
410
+ if (i + 1) % 10 == 0
411
+ s << " " + ((8 - i / 10).to_i).to_s + "\n"
412
+ end
413
+ end
414
+ 10.times { |i| s << ("a".ord + i).chr }
415
+ s << "\n[ wtm: #{@wtm} ]\n"
416
+ s << "[ castle: #{str_castle} ]\n"
417
+ s << "[ ep: #{@ep} ]\n\n"
418
+ puts s
419
+ end
420
+ end # class BoardCaparandom
421
+
422
+ end # module Shuriken