5 This file is part of Arrocco, which is Copyright 2007 Thomas Plick
6 (tomplick 'at' gmail.com).
8 Arrocco is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 Arrocco is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 import time
, ctypes
, thread
, Queue
, threading
25 from position
import *
29 def updatePVCache(pv
):
30 for (a
, b
) in zip(pv
, pv
[1:]):
34 def alphaBetaInC(pos
, depth
,
35 alpha
, beta
, stopper
= None, childArray
= None):
36 counter
= ctypes
.c_long(1)
37 if childArray
is None:
38 childArray
= (Position
* (depth
* 32))()
40 value
= lib
.alphaBeta(ctypes
.byref(pos
), childArray
, depth
,
41 alpha
, beta
, ctypes
.byref(counter
), ctypes
.byref(stopper
))
46 for i
in range(depth
):
48 node
= node
.children()[pos
.getBranch(i
)]
52 return dict(value
= value
, pv
= pv
,
53 nodeCount
= counter
.value
)
55 def alphaBeta(pos
, depth
, alpha
= -99999, beta
= 99999, splits
= 0,
56 stopper
= None, top
= False, parentProc
= None):
60 pos
.offswitch
.value
= 0
61 stopper
= pos
.offswitch
64 result
= alphaBetaSequential(pos
, depth
, alpha
, beta
, stopper
)
66 result
= alphaBetaParallel(pos
, depth
, alpha
, beta
, splits
, stopper
, parentProc
)
70 updatePVCache(result
['pv'])
73 result
['time'] = int(b
- a
) + 1
77 def alphaBetaSequential(pos
, depth
, alpha
, beta
, stopper
):
78 if depth
== 0 or not (pos
.fen() in pvCache
):
79 return alphaBetaInC(pos
, depth
, alpha
, beta
, stopper
)
81 favorite
= pvCache
[pos
.fen()]
82 children
= pos
.children()
83 children
.remove(favorite
)
84 children
.insert(0, favorite
)
90 childArray
= (Position
* (32 * depth
))()
92 for i
, child
in enumerate(children
):
94 result
= alphaBetaSequential(child
, nextDepth
, -beta
, -alpha
,
97 result
= alphaBetaInC(child
, nextDepth
, -beta
, -alpha
, stopper
,
98 childArray
= childArray
)
100 value
= -result
['value']
101 nodeCount
+= result
['nodeCount']
107 bestPV
= [pos
] + result
['pv']
109 return dict(value
= alpha
, pv
= bestPV
, nodeCount
= nodeCount
)
115 def alphaBetaParallel(pos
, depth
, alpha
, beta
, splits
, stopper
, parentProc
= None):
116 kids
= pos
.children()
117 gstopper
= ctypes
.c_int(0)
118 if depth
== 0 or len(kids
) == 0:
119 return alphaBeta(pos
, 0, alpha
, beta
, 0, gstopper
)
123 results
= Queue
.Queue()
124 for k
in kids
: k
.done
= False
130 if pos
.fen() in pvCache
:
131 favorite
= pvCache
[pos
.fen()]
132 kids
[kids
.index(favorite
)] = None
139 if k
is not None: q
.put(k
)
145 while rfq
< len(kids
):
146 if len(running
) < at_a_time
:
148 next
= q
.get_nowait().copy()
149 stopper
= ctypes
.c_int(0)
151 ret
= next
, proc
, alphaBeta(next
, depth
- 1, -beta
, -alpha
,
152 splits
- at_a_time
+ 1, stopper
, parentProc
)
153 if stopper
.value
== 0:
156 def stop(): stopper
.value
= 1
159 proc
= parentProc
.spawn(calc
, stop
)
161 proc
= spawn
.spawn(calc
, stop
)
171 resChild
, resProc
, result
= results
.get(True, .001)
172 if hasattr(resProc
, 'defunct'):
178 running
.remove(resProc
)
179 except ValueError: pass
183 value
= -result
['value']
184 nodeCount
+= result
['nodeCount']
187 if proc
.position
== resChild
:
197 bestPV
= [pos
] + result
['pv']
199 # evaluate other pos (if there is one) with this alpha
203 running.remove(proc)"""
205 # put that pos at the front of the queue
207 while not q
.empty(): newQ
.put(q
.get())
209 while not newQ
.empty(): q
.put(newQ
.get())
210 except KeyboardInterrupt:
211 for kid
in running
: kid
.stop()
214 for kid
in running
: kid
.stop()
215 return dict(value
= alpha
, nodeCount
= nodeCount
, pv
= bestPV
)