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 # XXX Fold into parallel function
56 def alphaBeta(pos
, depth
, alpha
= -99999, beta
= 99999, splits
= 0,
57 stopper
= None, top
= False, parentProc
= None):
60 pos
.offswitch
.value
= 0
61 result
= alphaBetaParallel(pos
, depth
, alpha
, beta
, splits
,
62 pos
.offswitch
, parentProc
)
64 updatePVCache(result
['pv'])
67 result
['time'] = int(b
- a
) + 1
74 def alphaBetaParallel(pos
, depth
, alpha
, beta
, splits
, stopper
, parentProc
= None):
76 if depth
== 0 or len(kids
) == 0 or stopper
.value
:
77 return alphaBetaInC(pos
, 0, alpha
, beta
, stopper
)
80 results
= Queue
.Queue()
81 for k
in kids
: k
.done
= False
87 if pos
.fen() in pvCache
:
88 favorite
= pvCache
[pos
.fen()]
89 kids
[kids
.index(favorite
)] = None
95 return alphaBetaInC(pos
, depth
, alpha
, beta
, stopper
)
98 if k
is not None: q
.put(k
)
102 while rfq
< len(kids
):
103 if len(running
) < at_a_time
:
105 next
= q
.get_nowait().copy()
106 stopper
= ctypes
.c_int(0)
108 ret
= next
, proc
, alphaBeta(next
, depth
- 1, -beta
, -alpha
,
109 splits
- at_a_time
+ 1, stopper
, parentProc
)
110 if stopper
.value
== 0: results
.put(ret
)
111 def stop(): stopper
.value
= 1
114 parentProc
= spawn
# module spawn
115 proc
= parentProc
.spawn(calc
, stop
)
118 proc
.position
, proc
.alpha
= next
, alpha
125 resChild
, resProc
, result
= results
.get(True, .05)
126 if hasattr(resProc
, 'defunct'):
130 if splits
> 0: at_a_time
= 2
131 running
.remove(resProc
)
132 except ValueError: pass
136 value
= -result
['value']
137 nodeCount
+= result
['nodeCount']
140 if proc
.position
== resChild
:
141 proc
.stop(); proc
.defunct
= True
149 bestPV
= [pos
] + result
['pv']
151 # evaluate other pos (if there is one) with this alpha
153 # put that pos at the front of the queue
155 while not q
.empty(): newQ
.put(q
.get())
157 while not newQ
.empty(): q
.put(newQ
.get())
158 except KeyboardInterrupt:
159 for kid
in running
: kid
.stop()
162 for kid
in running
: kid
.stop()
163 return dict(value
= alpha
, nodeCount
= nodeCount
, pv
= bestPV
)
166 def anotherOneStarted():
167 global threadsStarted
169 if threadsStarted
% 1000 == 0:
170 print "********* Started %s threads" % threadsStarted