Comments (9)
As discussed the issue is due to circular responsibilities of searcher and move ordering. Searcher depends on move ordering for AB-pruning but move ordering depends on searcher to update it's depedent attributes. To break this circular juggling I propose:
- We keep the existing
MoveOrder
(maybe renamed toMoveOrderingHeuristic
orMoveOrderingStrategy
) parent class withevaluate
signature:
def evaluate(self, move: chess.Move) -> float:
pass
FYI: TIL this is known as a strategy pattern.
- All child classes of
MoveOrder
, e.g.MvvLvaHeuristic
orKillerMoveHeurstic
now are built every time when we need to call order moves. We inject the dependencies on construction. All information now comes from the searcher, which means the searcher owns any dependencies. The runtime difference for doing this should be negligble because of python's pass by assignment principle.
For example for KillerMoveHeuristic
:
class KillerMoveHeuristic(MoveOrder):
def __init__(self, killer_table):
self._killer_table = killer_table
where killer_table
is now a MinimaxVariant
level object.
_ordered_moves
inMinimaxVariant
will take in theMoveOrder
child class as an argument as well, allowing it to decide which evaluate function to call.
@staticmethod
def _ordered_moves(move_ordering_strategy: MoveOrder, legal_moves: Any) -> Any:
return sorted(
legal_moves,
key=lambda move: (move_ordering_strategy.evaluate(move),),
reverse=True,
)
- We will move the
_build_moveorder
factory ontoMinimaxVariant
- and possibly change it to be a dispatching function, so that we can determine which move ordering we need once, and just call that create function over and over again. Whether this will need some sort of Dependency Injection is not quite clear to me yet, hopefully not.
from sporkfish.
where
killer_table
is now aMinimaxVariant
level object.What does level mean here?
@yibeili As in the killer_table will move into the MinimaxVariant
class, which will own the object (i.e. be responsible for it's creation and deletion).
from sporkfish.
i am not too sure :( my first question is that what problem will circular calling cause? and secondly, what if we have a separate class storing the info searcher wants from moveorder and the info moveorder from searcher?
@yibeili Circular design is usually avoided because it introduces tight coupling. Tight coupling causes rigidity; as you saw, each time we introduce a new type of move ordering, we need to extend the evaluate function. See Open/Closed principle. I'm sure chatGPT can give you more.
Re the second question, introducing a segway container class to hold your dependencies and inserting them when needed is called Dependency Injection. However this isn't much different to what I'm proposing here - instead of injecting them at run time and having mutable classes, we inject them on construction.
from sporkfish.
From the above it looks like PV, Hash/TT, and IID require information from the searcher in some way (the transposition table or PV approximation move). Moreover for history, killer and countermove heuristic, not only do we need this information from the searcher, we need to update their respective tables there too (i.e. we only update the table when there is a cutoff, which we can't possibly know in the move ordering class).
In the current evaluate function we only pass (board, move)
:
def evaluate(self, board: Board, move: chess.Move) -> float:
It looks like this won't be enough to encompass all cases. Any ideas on how to incorporate generically?
from sporkfish.
where
killer_table
is now aMinimaxVariant
level object.
What does level mean here?
from sporkfish.
i am not too sure :( my first question is that what problem will circular calling cause? and secondly, what if we have a separate class storing the info searcher wants from moveorder and the info moveorder from searcher?
from sporkfish.
Will discuss further details with you offline - the design looks quite nice to me, biggest question I have is that I am not sure how we can combine mover order strategies using this design. The static method:
@staticmethod def _ordered_moves(move_ordering_strategy: MoveOrder, legal_moves: Any) -> Any: return sorted( legal_moves, key=lambda move: (move_ordering_strategy.evaluate(move),), reverse=True, )
only takes in a single unique
move_ordering_strategy
, so how do we mix and match them?
@ccjeremylo Mixing and matching will be done through multiple inheritance or composition. Lets call it the CompositeMoveOrdering - this will have
class CompositeMoveOrdering(MoveOrder, MvvLva, Killer, ...)
or
class CompositeMoveOrdering(MoveOrder):
def __init__(MvvLva, Killer, ...):
from sporkfish.
Interesting - look forward to this 👀
from sporkfish.
Will discuss further details with you offline - the design looks quite nice to me, biggest question I have is that I am not sure how we can combine mover order strategies using this design. The static method:
@staticmethod
def _ordered_moves(move_ordering_strategy: MoveOrder, legal_moves: Any) -> Any:
return sorted(
legal_moves,
key=lambda move: (move_ordering_strategy.evaluate(move),),
reverse=True,
)
only takes in a single unique move_ordering_strategy
, so how do we mix and match them?
from sporkfish.
Related Issues (20)
- [Feature] Improve MVV-LVA performance
- [Investigation] Branching Factor analysis
- [Feature] lila EG tablebases HOT 1
- [Investigation] Futility pruning margin
- [Feature] Endgame tablebases handle repetition
- [Feature] Use/investigate best Docker image for performance
- [Design] Decorator pattern for Composite move order HOT 2
- [Feature] Make Composite move ordering configurable HOT 1
- [Discuss] MoveOrderFactory
- [Investigation] Perf/Tests analysis for Killer moves/History heuristic
- [Feature] Consider moving init of move ordering weights into class level HOT 1
- [Feature] Synchronously handle opponentGone HOT 1
- [Feature] CompositeTablebase
- [Feature] Refactor Lazy SMP to take in different single process search functions
- [Feature] MTD(f) (negamax variant)
- [Feature] Hand-crafted evaluation
- [Feature] Package project properly
- [Feature] Add "captured_piece" on the board
- [Design] Redesign for stats HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sporkfish.