Commit fc8a6bcc authored by Jan Pokorný's avatar Jan Pokorný 💬

templated PNS search

parent 875201f6
#include "PNS.hpp"
#include "TranspositionTable/TranspositionTableHashed.hpp"
using namespace std;
TranspositionTableHashed<Qubic> tt(100'000'000);
// from GAME-TREE SEARCH USING PROOF NUMBERS: THE FIRST TWENTY YEARS
Node::Node(const Qubic &game, bool isAndNode, Node *parent) :
pn(SaturatedUInt::INFTY), dn(SaturatedUInt::INFTY), game(game), isAndNode(isAndNode), parent(parent), depth(-1) {}
Node::Node(const Qubic &game) : Node(game, !game.firstPlayerPlays(), nullptr) {}
bool Node::operator==(const Node &other) const {
return this->game == other.game;
}
bool Node::operator!=(const Node &other) const {
return !(*this == other);
}
bool Node::isExpanded() const {
return children.size() != 0;
}
GameState Node::getGameState() const {
if (pn == 0) {
return FirstWon;
} else if (dn == 0) {
return SecondWonOrDraw;
}
return Open;
}
void Node::setProofAndDisproofNumbers() {
GameState tt_result = tt.getGameState(this->game);
if (tt_result != Open) {
setProofAndDisproofNumbers(tt_result);
depth =tt.getVal(this->game);
return;
}
if (this->isExpanded()) { /* interior node */
if (this->isAndNode) {
this->pn = 0;
this->dn = SaturatedUInt::INFTY;
int max_depth = -1;
for (Node &c : this->children) { // check speed
this->pn = this->pn + c.pn;
this->dn = std::min(this->dn, c.dn);
max_depth = std::max(max_depth, c.depth);
if(c.dn == 0){
this->depth = c.depth+1;
}
}
if(this->pn == 0){
this->depth = max_depth+1;
}
} else { /* OR node */
this->pn = SaturatedUInt::INFTY;
this->dn = 0;
int max_depth = -1;
for (Node &c : this->children) {
this->dn = this->dn + c.dn;
this->pn = std::min(this->pn, c.pn);
max_depth = std::max(max_depth, c.depth);
if(c.pn == 0){
this->depth = c.depth+1;
}
}
if(this->dn == 0){
this->depth = max_depth+1;
}
}
} else { /* terminal node or non-terminal leaf */
if (this->parent != nullptr) {
setProofAndDisproofNumbers(this->game.getState(this->parent->game));
} else {
setProofAndDisproofNumbers(this->game.getState());
}
// it is terminal leaf
if(this->getGameState() != Open){
depth = 0;
}
}
}
void Node::setProofAndDisproofNumbers(GameState state) {
switch (state) {
case SecondWonOrDraw:
this->pn = SaturatedUInt::INFTY;
this->dn = 0;
break;
case FirstWon:
this->pn = 0;
this->dn = SaturatedUInt::INFTY;
break;
case Open:
this->pn = 1;
this->dn = 1;
break;
default:
throw std::invalid_argument("Unknown gamestate");
}
}
Node *Node::updateAncestors() {
Node *current = this;
while (true) {
SaturatedUInt oldpn = current->pn;
SaturatedUInt olddn = current->dn;
current->setProofAndDisproofNumbers();
// adding to TT if the position was solved
if (current->getGameState() != Open) {
tt.add(current->game, current->getGameState(), current->depth);
}
if ((current->pn == oldpn && current->dn == olddn) || current->parent == nullptr) {
return current;
}
current = current->parent;
}
}
Node *Node::selectMostProvingNode() {
Node *current = this;
while (current->isExpanded()) {
SaturatedUInt value = SaturatedUInt::INFTY;
Node *best = nullptr;
if (current->isAndNode) {
for (Node &child : current->children) {
if (child.getGameState() != Open) { // is this sound?
continue;
}
if (value > child.dn) {
best = &child;
value = child.dn;
}
}
} else { /* OR node */
for (Node &child : current->children) {
if (child.getGameState() != Open) { // Is this sound?
continue;
}
if (value > child.pn) {
best = &child;
value = child.pn;
}
}
}
if (best == nullptr) {
cerr << "No MPN found" << endl;
cerr << *current;
for (Node &child : current->children) {
cerr << child;
}
throw logic_error("No MPN found");
}
current = best;
}
return current;
}
void PNS(Node &root) {
int c = 0;
root.setProofAndDisproofNumbers();
Node *current = &root;
while (root.pn != 0 && root.dn != 0) {
Node *mostProving = current->selectMostProvingNode();
mostProving->expand();
current = mostProving->updateAncestors();
++c;
}
cerr << c << endl;
}
void Node::expand() {
vector<Qubic> children_games = this->game.generateChildren();
for (Qubic &child_game : children_games) {
Node child = Node(child_game, !this->isAndNode, this);
child.setProofAndDisproofNumbers();
children.push_back(child);
if ((this->isAndNode && child.dn == 0) || (!this->isAndNode && child.pn == 0)) {
break;
}
}
}
ostream &operator<<(ostream &os, const Node &n) {
os << "Type: " << (n.isAndNode ? "AND" : "OR") << endl;
os << "pn: " << (uint)n.pn << endl;
os << "dn: " << (uint)n.dn << endl;
os << "State: " << GameStateToString(n.getGameState()) << endl;
os << n.game;
return os;
}
\ No newline at end of file
#include <cstdint>
#include <vector>
#include <iostream>
#include "Qubic.hpp"
#include "SaturatedUInt.hpp"
struct Node;
#include "TranspositionTable/TranspositionTableHashed.hpp"
void PNS(Node &root);
using namespace std;
template <typename Game>
struct Node {
private:
Node(const Qubic &game, bool isAndNode, Node *parent);
Node(const Game &game, bool isAndNode, Node *parent) :
pn(SaturatedUInt::INFTY), dn(SaturatedUInt::INFTY), game(game), isAndNode(isAndNode), parent(parent), depth(-1) {}
public:
Node(const Qubic &game);
Node(const Game &game) : Node(game, !game.firstPlayerPlays(), nullptr) {}
SaturatedUInt pn, dn; // TODO calculate whats enough size(type) for particular game
// pn = 0 - win for 1st player
// dn = 0 - draw or win for 2nd player
const Qubic game;
const Game game;
const bool isAndNode;
Node *const parent;
vector<Node> children;
int depth;
bool operator==(const Node &other) const;
bool operator==(const Node &other) const {
return this->game == other.game;
}
bool operator!=(const Node &other) const {
return !(*this == other);
}
bool operator!=(const Node &other) const;
bool isExpanded() const {
return children.size() != 0;
}
GameState getGameState() const {
if (pn == 0) {
return FirstWon;
} else if (dn == 0) {
return SecondWonOrDraw;
}
return Open;
}
bool isExpanded() const;
void setProofAndDisproofNumbers(TranspositionTableHashed<Game> & tt) {
GameState tt_result = tt.getGameState(this->game);
if (tt_result != Open) {
setProofAndDisproofNumbers(tt_result);
depth =tt.getVal(this->game);
return;
}
if (this->isExpanded()) { /* interior node */
if (this->isAndNode) {
this->pn = 0;
this->dn = SaturatedUInt::INFTY;
int max_depth = -1;
for (Node &c : this->children) { // check speed
this->pn = this->pn + c.pn;
this->dn = std::min(this->dn, c.dn);
max_depth = std::max(max_depth, c.depth);
if(c.dn == 0){
this->depth = c.depth+1;
}
}
if(this->pn == 0){
this->depth = max_depth+1;
}
} else { /* OR node */
this->pn = SaturatedUInt::INFTY;
this->dn = 0;
int max_depth = -1;
for (Node &c : this->children) {
this->dn = this->dn + c.dn;
this->pn = std::min(this->pn, c.pn);
max_depth = std::max(max_depth, c.depth);
if(c.pn == 0){
this->depth = c.depth+1;
}
}
if(this->dn == 0){
this->depth = max_depth+1;
}
}
} else { /* terminal node or non-terminal leaf */
if (this->parent != nullptr) {
setProofAndDisproofNumbers(this->game.getState(this->parent->game));
} else {
setProofAndDisproofNumbers(this->game.getState());
}
// it is terminal leaf
if(this->getGameState() != Open){
depth = 0;
}
}
}
GameState getGameState() const;
void setProofAndDisproofNumbers(GameState state) {
switch (state) {
case SecondWonOrDraw:
this->pn = SaturatedUInt::INFTY;
this->dn = 0;
break;
case FirstWon:
this->pn = 0;
this->dn = SaturatedUInt::INFTY;
break;
case Open:
this->pn = 1;
this->dn = 1;
break;
default:
throw std::invalid_argument("Unknown gamestate");
}
}
void expand();
void setProofAndDisproofNumbers();
void setProofAndDisproofNumbers(GameState state);
Node *selectMostProvingNode();
Node *updateAncestors();
Node *updateAncestors(TranspositionTableHashed<Game> & tt) {
Node *current = this;
while (true) {
SaturatedUInt oldpn = current->pn;
SaturatedUInt olddn = current->dn;
current->setProofAndDisproofNumbers(tt);
// adding to TT if the position was solved
if (current->getGameState() != Open) {
tt.add(current->game, current->getGameState(), current->depth);
}
if ((current->pn == oldpn && current->dn == olddn) || current->parent == nullptr) {
return current;
}
current = current->parent;
}
}
friend std::ostream &operator<<(std::ostream &os, const Node &n);
Node *selectMostProvingNode() {
Node *current = this;
while (current->isExpanded()) {
SaturatedUInt value = SaturatedUInt::INFTY;
Node *best = nullptr;
if (current->isAndNode) {
for (Node &child : current->children) {
if (child.getGameState() != Open) { // is this sound?
continue;
}
if (value > child.dn) {
best = &child;
value = child.dn;
}
}
} else { /* OR node */
for (Node &child : current->children) {
if (child.getGameState() != Open) { // Is this sound?
continue;
}
if (value > child.pn) {
best = &child;
value = child.pn;
}
}
}
if (best == nullptr) {
cerr << "No MPN found" << endl;
cerr << *current;
for (Node &child : current->children) {
cerr << child;
}
throw logic_error("No MPN found");
}
current = best;
}
return current;
}
void expand( TranspositionTableHashed<Game> & tt) {
vector<Game> children_games = this->game.generateChildren();
for (Game &child_game : children_games) {
Node child = Node(child_game, !this->isAndNode, this);
child.setProofAndDisproofNumbers(tt);
children.push_back(child);
if ((this->isAndNode && child.dn == 0) || (!this->isAndNode && child.pn == 0)) {
break;
}
}
}
template <typename G>
friend std::ostream &operator<<(std::ostream &os, const Node<G> &n);
};
template <typename Game>
ostream &operator<<(ostream &os, const Node<Game> &n) {
os << "Type: " << (n.isAndNode ? "AND" : "OR") << endl;
os << "pn: " << (uint)n.pn << endl;
os << "dn: " << (uint)n.dn << endl;
os << "State: " << GameStateToString(n.getGameState()) << endl;
os << n.game;
return os;
}
template <typename Game>
void PNS(Node<Game> &root, TranspositionTableHashed<Game> & tt) {
int c = 0;
root.setProofAndDisproofNumbers(tt);
Node<Game> *current = &root;
while (root.pn != 0 && root.dn != 0) {
Node<Game> *mostProving = current->selectMostProvingNode();
mostProving->expand(tt);
current = mostProving->updateAncestors(tt);
++c;
}
cerr << c << endl;
}
\ No newline at end of file
......@@ -4,7 +4,7 @@
#include "GameState.hpp"
#include "PNS.hpp"
#include "Qubic.hpp"
#include "df-pn.hpp"
#include "TranspositionTable/TranspositionTableHashed.hpp"
int main(void) {
Qubic::generateLines();
......@@ -16,8 +16,9 @@ int main(void) {
throw;
}
Node root(game);
PNS(root);
Node<Qubic> root(game);
TranspositionTableHashed<Qubic> tt(100'000'000);
PNS(root, tt);
cout << GameStateToString(root.getGameState()) << endl;
cerr << root.depth << endl;
return 0;
......
#include "PNS.hpp"
#include "TranspositionTable/TranspositionTableHashed.hpp"
#include "Qubic.hpp"
#include <cassert>
int main(void) {
Qubic::generateLines();
TranspositionTableHashed<Qubic> tt;
Node gameNodeEasy = Node(Qubic(0x7, 0x70));
PNS(gameNodeEasy);
Node<Qubic> gameNodeEasy = Node<Qubic>(Qubic(0x7, 0x70));
PNS(gameNodeEasy, tt);
assert(gameNodeEasy.getGameState() == FirstWon);
assert(gameNodeEasy.depth == 1);
Node gameNode = Node(Qubic(0xc84081b180410000, 0xb01c4010909000));
gameNode.expand();
gameNode.setProofAndDisproofNumbers();
Node<Qubic> gameNode = Node<Qubic>(Qubic(0xc84081b180410000, 0xb01c4010909000));
gameNode.expand(tt);
gameNode.setProofAndDisproofNumbers(tt);
assert(gameNode.getGameState() == SecondWonOrDraw);
return 0;
}
\ No newline at end of file
#pragma once
#include <unordered_map>
#include <vector>
#include <cassert>
#include "../GameState.hpp"
......@@ -27,7 +29,7 @@ class TranspositionTableHashed {
if (tt.count(game) > 0) {
return tt.at(game).second;
}
return NAN;
assert(0 && "Value not in tt");
}
void eraseWorst(double perc){
......@@ -38,7 +40,7 @@ class TranspositionTableHashed {
maxval = std::max(maxval, p.second.second);
}
double treshold = minval + perc*(maxval-minval);
vector<Game> to_delete;
std::vector<Game> to_delete;
for( const auto & p : tt){
if(p.second.second <= treshold){
to_delete.push_back(p.first);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment