diff --git a/CMakeLists.txt b/CMakeLists.txt index e31e2c8..c1fe1ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ target_sources(oink src/zlkpp.cpp src/ptl.cpp src/dtl.cpp + src/dftl.cpp ${OINK_HDRS} ) @@ -178,6 +179,10 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) add_executable(counter_qpt src/tools/counter_qpt.cpp) set_target_props(counter_qpt) + add_executable(counter_odftl src/tools/counter_odftl.cpp) + set_target_props(counter_odftl) + target_link_libraries(counter_odftl oink) + add_executable(tc src/tools/tc.cpp) set_target_props(tc) target_link_libraries(tc oink) @@ -225,6 +230,8 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) add_test(NAME TestSolverSPPTL COMMAND test_solvers ${CMAKE_CURRENT_SOURCE_DIR}/tests --spptl) add_test(NAME TestSolverDTL COMMAND test_solvers ${CMAKE_CURRENT_SOURCE_DIR}/tests --dtl) add_test(NAME TestSolverIDTL COMMAND test_solvers ${CMAKE_CURRENT_SOURCE_DIR}/tests --idtl) + add_test(NAME TestSolverDFTL COMMAND test_solvers ${CMAKE_CURRENT_SOURCE_DIR}/tests --dftl) + add_test(NAME TestSolverODFTL COMMAND test_solvers ${CMAKE_CURRENT_SOURCE_DIR}/tests --odftl) # test ZLK variations add_test(NAME TestSolverZLKseq COMMAND test_solvers ${CMAKE_CURRENT_SOURCE_DIR}/tests --zlk -w -1) add_test(NAME TestSolverZLKpar COMMAND test_solvers ${CMAKE_CURRENT_SOURCE_DIR}/tests --zlk -w 0) diff --git a/README.md b/README.md index d0acf96..c29c805 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ PTL | Progressive Tangle Learning (research variant) SPPTL | Single-player Progressive Tangle Learning (research variant) DTL | Distance Tangle Learning (research variant based on 'good path length') IDTL | Interleaved Distance Tangle Learning (interleaved variant of DTL) +DFTL | Distraction-Free Tangle Learning (two-player variant) +ODFTL | One-player Distraction-Free Tangle Learning ### Fixpoint algorithms @@ -195,6 +197,7 @@ counter\_rob | SCC version of counter\_core. counter\_dtl | Counterexample to the DTL solver counter\_ortl | Counterexample to the ORTL solver counter\_symsi | Counterexample of Matthew Maat to (standard) symmetric strategy improvement +counter\_dftl | Counterexample to the DFTL solver tc | Two binary counters generator (game family that is an exponential lower bound for many algorithms). See also Tom van Dijk (2019) [A Parity Game Tale of Two Counters](https://doi.org/10.4204/EPTCS.305.8). In: GandALF 2019. tc+ | TC modified to defeat the RTL solver @@ -212,7 +215,7 @@ The two binary counters game family appears to be a quasi-polynomial lower bound * The progress measures variations SSPM, QPT Some algorithms can solve the two binary counters games in polynomial time: -* The tangle learning algorithms RTL, ORTL, PTL, SPPTL, DTL, IDTL +* The tangle learning algorithms RTL, ORTL, PTL, SPPTL, DTL, IDTL, DFTL, PDFTL * The strategy improvement algorithm PSI * Unsure: the BSSPM and BQPT algorithms diff --git a/src/dftl.cpp b/src/dftl.cpp new file mode 100644 index 0000000..7a2abc4 --- /dev/null +++ b/src/dftl.cpp @@ -0,0 +1,818 @@ +/* + * Copyright 2020-2024 Tom van Dijk + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dftl.hpp" + +namespace pg { + +/** + * The "distraction-free" tangle learning algorithm. + * + * PROCEDURE iteration(player) + * 1. Take all remaining vertices V as targets + * 2. Tangle-attract to V from top to bottom. + * 3. Closed regions of become tangles + * 4. (Optional: Repeat 2-3 until no more closed regions.) + * 5. Remove the lowest 's region top from V if that region was open. + * 6. Repeat 2-5 until there are no more regions of , or no vertices of in V + * + * Here, player==0 or player==1 or player==-1 meaning both players. + * + * Possible executions: + * A. Run iteration(even) until no new tangles found; then iteration(odd) until game is solved + * B. Run iteration(odd) until no new tangles found; then iteration(even) until game is solved + * C. Run iteration(even) once then iteration(odd) once; repeat until game is solved + * D. Run iterations for both players simultaneously until the game is solved. + * + * Each iteration of this algorithm runs in polynomial time and consists of O(n²) many "steps". + * Each "step" (2-3) is simply one iteration of single-player tangle learning. + * After each step, either there are fewer regions, or V is smaller. + * + * Each DFTL iteration starts with V set to all even-priority (or odd-priority) vertices. + * Each step then decomposes the game into regions dominated by the vertices in V. + * That is, starting with the highest v in V, attract all vertices+tangles with priority <= pr(v). + * Each highest vertex in v (the target of the attractor) is called the top vertex of its region. + * Every *closed* region is then analysed to find new tangles, as in standard tangle learning. + * If however no region is closed, then we assume that at least one vertex in V is a distraction. + * We assume that "surely" the lowest top vertex is distracting, so we remove that vertex from V. + * + * Every step thus either removes a vertex from V, or learns new tangles (removing a region). + * Hence each iteration has at most O(n²) steps and resuilts in at most O(n²) new tangles. + * + * For executions A (and B), the counter_dftl generator is an exponential lower bound. + * For execution D, the tc+ generator is an exponential lower bound. + * For execution C, no lower bound is yet known. + */ + + +DFTLSolver::DFTLSolver(Oink *oink, Game *game) : Solver(oink, game) +{ +} + + +DFTLSolver::~DFTLSolver() +{ +} + + +/** + * Attract as player via to , vertices in from subgame . + * If >= 0, then only attract vertices with priority 0 <= pr <= max_prio. + * Add attracted vertices to and to queue . + * Update with the obtained attractor strategy. + */ +void +DFTLSolver::attractVertices(const int pl, const int v, bitset &R, bitset &Z, bitset &G, const int max_prio) +{ + // attract vertices with an edge to + for (auto curedge = ins(v); *curedge != -1; curedge++) { + int from = *curedge; + if (Z[from]) { + // already in Z, maybe set strategy (for vertices in the original target set) + if (owner(from) == pl and str[from] == -1) str[from] = v; + } else if (R[from] and (max_prio < 0 or priority(from) <= max_prio)) { + if (owner(from) != pl) { + // check if opponent can escape + bool escapes = false; + for (auto curedge = outs(from); *curedge != -1; curedge++) { + int to = *curedge; + if (G[to] and !Z[to]) { + escapes = true; + break; + } + } + if (escapes) continue; + } + // attract + Z[from] = true; + str[from] = owner(from) == pl ? v : -1; + Q.push(from); +#ifndef NDEBUG + // maybe report event + if (trace >= 3) { + logger << "\033[1;37mattracted \033[36m" << label_vertex(from) << "\033[m by \033[1;36m" << pl << "\033[m"; + if (owner(from) == pl) logger << " (via " << label_vertex(v) << ")" << std::endl; + else logger << " (forced)" << std::endl; + } +#endif + } + } +} + + +/** + * Try to attract tangle for player to attractor set . + * All vertices in tangle must be in and the opponent may not escape to subgame . + * Add attracted vertices to and to queue . + * Update with the obtained attractor strategy. + */ +bool +DFTLSolver::attractTangle(const int t, const int pl, bitset &R, bitset &Z, bitset &G, const int max_prio) +{ + /** + * Check if tangle is won by player and not deleted. + */ + { + const int tangle_pr = tpr[t]; + if (tangle_pr == -1) return false; // deleted tangle + if (pl != -1 and pl != (tangle_pr&1)) return false; // not of desired parity + } + + /** + * Check if tangle is contained in Z+R and if any vertices are not already in Z + * If no new vertices could be attracted, then don't attract... + */ + { + bool can_attract_new = false; + int *ptr = tv[t]; + for (;;) { + const int v = *ptr++; + if (v == -1) break; + ptr++; // skip strategy + if (!this->G[v]) { + // on-the-fly detect out-of-game tangles + tpr[t] = -1; // delete the tangle + return false; // is now a deleted tangle + } else if (Z[v]) { + continue; // already attracted + } else if (!R[v]) { + return false; // not contained in Z+R + } else if (max_prio >= 0 and priority(v) > max_prio) { + return false; + } else { + can_attract_new = true; // has vertices not yet attracted + } + } + if (!can_attract_new) return false; + } + + /** + * Check if the tangle can escape to G\Z. + */ + { + int v, *ptr = tout[t]; + while ((v=*ptr++) != -1) { + if (Z[v]) continue; + if (G[v]) return false; // opponent escapes + } + } + + /** + * Attract! + */ + { + int *ptr = tv[t]; + for (;;) { + const int v = *ptr++; + if (v == -1) break; + const int s = *ptr++; + if (Z[v]) continue; // already in + Z[v] = true; + str[v] = s; + Q.push(v); + +#ifndef NDEBUG + // maybe report event + if (trace >= 3) { + logger << "\033[1;37mattracted \033[36m" << label_vertex(v) << "\033[m by \033[1;36m" << pl << "\033[m"; + logger << " (via tangle " << t << ")" << std::endl; + } +#endif + } + } + + return true; +} + + +/** + * Attract vertices that are in tangles. + * Current attracting set is . + * Current subgame is . + * Only tangles that are contained in + and vertices of maximum priority max_prio (if >= 0) + * Write strategy to . + * Current attracting vertex is . + * Attracting for player . + * (for trace) Attracting to region with priority . + */ +inline void +DFTLSolver::attractTangles(const int pl, int v, bitset &R, bitset &Z, bitset &G, const int max_prio) +{ + const auto &in_cur = tin[v]; + for (int from : in_cur) attractTangle(from, pl, R, Z, G, max_prio); +} + + +/** + * Compute SCCs in subgraph induced by and . + * Start the SCC computation at vertex . + * Every SCC is then processed as a tangle. + * If the tangle is closed, it is a dominion and added to and . + */ +bool +DFTLSolver::extractTangles(int startvertex, bitset &R, int *str) +{ + bool new_tangles = false; + const int pr = priority(startvertex); + const int pl = pr&1; + + /** + * The following is the nonrecursive implementation of David Pearce, + * "A space-efficient algorithm for finding strongly connected components" (IPL, 2016), + * modified to on-the-fly restrict the graph by and . + */ + + // beginVisiting + pea_vS.push(startvertex); + pea_iS.push(0); + pea_root[startvertex] = true; + pea_vidx[startvertex] = pea_curidx++; + while (pea_vS.nonempty()) { +pearce_again: + // visitLoop + const unsigned int n = pea_vS.back(); + unsigned int i = pea_iS.back(); + + if (owner(n) != pl) { + auto edges = outs(n); + if (i>0) { + // finishEdge + const int w = edges[i-1]; + if (pea_vidx[w] < pea_vidx[n]) { + pea_vidx[n] = pea_vidx[w]; + pea_root[n] = false; + } + } + for (;;) { + const int to = edges[i]; + if (to == -1) break; // done + // beginEdge + if (R[to]) { + if (pea_vidx[to] == 0) { + pea_iS.back() = i+1; + // beginVisiting + pea_vS.push(to); + pea_iS.push(0); + pea_root[to] = true; + pea_vidx[to] = pea_curidx++; + goto pearce_again; // break; continue; + } else { + // finishEdge + if (pea_vidx[to] < pea_vidx[n]) { + pea_vidx[n] = pea_vidx[to]; + pea_root[n] = false; + } + } + } + i++; + } + } else { + const int s = str[n]; + if (i == 0) { + // beginEdge + if (pea_vidx[s] == 0) { + pea_iS.back() = 1; + // beginVisiting + pea_vS.push(s); + pea_iS.push(0); + pea_root[s] = true; + pea_vidx[s] = pea_curidx++; + goto pearce_again; // break; continue; + } + } + // finishEdge + if (pea_vidx[s] < pea_vidx[n]) { + pea_vidx[n] = pea_vidx[s]; + pea_root[n] = false; + } + } + // finishVisiting + pea_vS.pop(); + pea_iS.pop(); + if (pea_root[n]) { + pea_curidx -= 1; + tangle.push_back(n); + while (pea_S.nonempty()) { + const int t = pea_S.back(); + if (pea_vidx[n]>pea_vidx[t]) break; + pea_S.pop(); + pea_curidx -= 1; + pea_vidx[t] = (unsigned int)-1; + tangle.push_back(t); + } + pea_vidx[n] = (unsigned int)-1; + } else { + pea_S.push(n); + continue; + } + + /** + * End of Pearce's algorithm. + * At this point, we have an SCC in . + * + * Now check if the SCC is nontrivial, i.e., contains a cycle, i.e., either 2+ vertices + * or a self-loop. + */ + + const bool is_tangle = (tangle.size() > 1) or + ((unsigned int)str[n] == n) or + (str[n] == -1 and game->has_edge(n, n)); + if (!is_tangle) { + tangle.clear(); + continue; + } + + /** + * We have a tangle. Compute the outgoing edges (into ) and the next highest region. + */ + + for (const int v : tangle) escapes[v] = true; + + for (const int v : tangle) { + if (owner(v) != pl) { + for (auto curedge = outs(v); *curedge != -1; curedge++) { + int to = *curedge; + if (G[to] and !escapes[to]) { + escapes[to] = true; + tangleto.push(to); + } + } + } + } + + escapes.reset(); + + /** + * If there are no outgoing edges, then we have found a dominion. + */ + + if (tangleto.empty()) { + // dominion + if (trace) { + logger << "\033[1;38;5;201mdominion \033[36m" << pr << "\033[m"; +#ifndef NDEBUG + if (trace >= 2) { + for (const int v : tangle) { + logger << " \033[1;36m" << label_vertex(v) << "\033[m"; + if (str[v] != -1) logger << "->" << label_vertex(str[v]); + } + } +#endif + logger << std::endl; + } + for (const int v : tangle) { + // if not yet added to solve queue, mark and add it + auto &S = pl == 0 ? S0 : S1; + auto &dom_vector = pl == 0 ? dom_vector_0 : dom_vector_1; + if (S[v] == false) { + S[v] = true; + dom_vector.push_back(v); + dom_vector.push_back(str[v]); + } + } + dominions++; + tangle.clear(); + new_tangles = true; + continue; + } + + /** + * We're not a dominion, we're a tangle. + */ + + if (trace >= 1) { + logger << "\033[1;38;5;198mnew tangle " << pr << "\033[m (" << tpr.size() << ")"; + if (trace >= 2) { + for (const int v : tangle) { + logger << " \033[1;36m" << label_vertex(v) << "\033[m"; + if (str[v] != -1) logger << "->" << label_vertex(str[v]); + } + } + logger << " with " << tangleto.size() << " escape vertices."; + logger << std::endl; + } + + // new tangle idx + const int tidx = tpr.size(); + + // add back links to all normal vertices in our [out] + for (unsigned int x = 0; x < tangleto.size(); x++) tin[tangleto[x]].push_back(tidx); + + // move tangleto into vout + int* _tout = new int[tangleto.size()+1]; + std::copy(&tangleto[0], &tangleto[tangleto.size()], _tout); + _tout[tangleto.size()] = -1; + tout.push_back(_tout); + + // move tangle into vv + int* _vv = new int[tangle.size()*2+1], c=0; + for (const int v : tangle) { + _vv[c++] = v; + _vv[c++] = str[v]; + } + _vv[c] = -1; + tv.push_back(_vv); + + // and set p to pr + tpr.push_back(pr); + + tangles++; + new_tangles = true; + tangle.clear(); + tangleto.clear(); + } + + pea_S.clear(); + return new_tangles; +} + +/** + * Two-player tangle learning. + * Find new tangles for given a set of vertices in the subgame . + * is typically the full remaining game. + */ +bool +DFTLSolver::tl(bitset &V, bitset &R, const int player, int &lowest_open_0, int &lowest_open_1) +{ + bool changes = false; + lowest_open_0 = -2; + lowest_open_1 = -2; + + for (auto top = V.find_last(); top != bitset::npos; top = V.find_prev(top)) { + if (!R[top]) continue; + + Z[top] = true; // add to + str[top] = -1; + Q.push(top); + + const int pl = priority(top) & 1; + + while (Q.nonempty()) { + const int v = Q.pop(); + R[v] = false; // remove from + attractVertices(pl, v, R, Z, R, priority(top)); + attractTangles(pl, v, R, Z, R, priority(top)); + } + +#ifndef NDEBUG + if (trace >= 2) { + // report region + logger << "\033[1;33mregion\033[m \033[1;36m" << priority(top) << "\033[m"; + for (auto v = Z.find_last(); v != bitset::npos; v = Z.find_prev(v)) { + logger << " \033[1;38;5;15m" << label_vertex(v) << "\033[m"; + if (str[v] != -1) logger << "->" << label_vertex(str[v]); + } + logger << std::endl; + } +#endif + + bool closed_region = true; + + if (owner(top) == pl) { + closed_region = (str[top] != -1); + } else { + for (auto curedge = outs(top); *curedge != -1; curedge++) { + if (R[*curedge]) { + closed_region = false; + break; + } + } + } + + if (closed_region) { + // comment the following line for a variation that removes + // top vertices of the lowest region that is open, instead of + // the lowest region *if* it is open + (pl == 0 ? lowest_open_0 : lowest_open_1) = -1; + + if (player == -1 or pl == player) { + // Extract tangles by computing bottom SCCs starting at each top vertex. + // Note: each bottom SCC must contain a top vertex. + std::fill(pea_vidx, pea_vidx+nodecount(), '\0'); + pea_curidx = 1; + if (extractTangles(top, Z, str)) changes = true; + } + } else { + (pl == 0 ? lowest_open_0 : lowest_open_1) = top; + } + + Z.reset(); + } + + return changes; +} + + +/** + * Single-player tangle learning. + * Find new tangles for given a set of vertices in the subgame . + * is typically the full remaining game. + */ +bool +DFTLSolver::sptl(bitset &V, bitset &R, const int player, int &lowest_top) +{ + bool changes = false; + lowest_top = -1; + + for (auto top = V.find_last(); top != bitset::npos; top = V.find_prev(top)) { + if (!R[top]) continue; + + lowest_top = top; + Z[top] = true; // add to + str[top] = -1; + Q.push(top); + + while (Q.nonempty()) { + const int v = Q.pop(); + R[v] = false; // remove from + attractVertices(player, v, R, Z, R, priority(top)); + attractTangles(player, v, R, Z, R, priority(top)); + } + +#ifndef NDEBUG + if (trace >= 2) { + // report region + logger << "\033[1;33mregion\033[m \033[1;36m" << priority(top) << "\033[m"; + for (auto v = Z.find_last(); v != bitset::npos; v = Z.find_prev(v)) { + logger << " \033[1;38;5;15m" << label_vertex(v) << "\033[m"; + if (str[v] != -1) logger << "->" << label_vertex(str[v]); + } + logger << std::endl; + } +#endif + + bool closed_region = true; + + if (owner(top) == player) { + closed_region = (str[top] != -1); + } else { + for (auto curedge = outs(top); *curedge != -1; curedge++) { + if (R[*curedge]) { + closed_region = false; + break; + } + } + } + + if (closed_region) { + // Extract tangles by computing bottom SCCs starting at each top vertex. + // Note: each bottom SCC must contain a top vertex. + std::fill(pea_vidx, pea_vidx+nodecount(), '\0'); + pea_curidx = 1; + if (extractTangles(top, Z, str)) changes = true; + } + + Z.reset(); + } + + return changes; +} + + +void +DFTLSolver::partition(bitset &R, int top, bitset &Even, bitset &Odd) +{ + for (; top!=-1 ;top--) { + if (!R[top]) continue; + + const int pl = priority(top)&1; + auto &Z = pl == 0 ? Even : Odd; + +#ifndef NDEBUG + if (trace >= 2) W = Z; +#endif + + Z[top] = true; // add to + str[top] = -1; + Q.push(top); + + while (Q.nonempty()) { + const int v = Q.pop(); + R[v] = false; // remove from + attractVertices(pl, v, R, Z, R, priority(top)); + attractTangles(pl, v, R, Z, R, priority(top)); + } + +#ifndef NDEBUG + if (trace >= 2) { + // report region + W ^= Z; + logger << "\033[1;33mregion\033[m "; + logger << "\033[1;36m" << priority(top) << "\033[m"; + for (auto v = W.find_last(); v != bitset::npos; v = W.find_prev(v)) { + logger << " \033[1;38;5;15m" << label_vertex(v) << "\033[m"; + if (str[v] != -1) logger << "->" << label_vertex(str[v]); + } + logger << std::endl; + } +#endif + } +} + + +bool +DFTLSolver::search(const int player) +{ + const int T = tangles; + const int D = dominions; + + // just start with V is all 's vertices + V = G; + bitset U(V); + if (player == 0) U -= Parity; + if (player == 1) U &= Parity; + + while (U.any()) { + steps++; + + CurG = G; + int lowest_open_0, lowest_open_1; + if (tl(V, CurG, player, lowest_open_0, lowest_open_1)) { + // Extend any dominions that were found. + // (Any solved vertices are now in and .) + for (int pl = 0; pl < 2; pl++) { + auto &S = pl == 0 ? S0 : S1; + auto &dom_vector = pl == 0 ? dom_vector_0 : dom_vector_1; + if (!dom_vector.empty()) { + for (unsigned i = 0; isolve(v, pl, str[v]); + attractVertices(pl, v, G, S, G, -1); + attractTangles(pl, v, G, S, G, -1); + } + + V -= S; // remove from V + U -= S; // remove from U + G -= S; // remove from G + S.reset(); + } + } + // continue; // uncomment to repeat TL until no new tangles are found + // otherwise: remove lowest open even if higher regions are closed + } + // logger << "lowest open 0 is " << lowest_open_0 << " and lowest open 1 is " << lowest_open_1 << std::endl; + if (lowest_open_0 >= 0 && player != 1) { + V[lowest_open_0] = false; + U[lowest_open_0] = false; +#ifndef NDEBUG + if (trace >= 1) { + logger << "\033[1;38;5;33mremoving \033[36m" << label_vertex(lowest_open_0) << "\033[m" << std::endl; + } +#endif + } + if (lowest_open_1 >= 0 && player != 0) { + V[lowest_open_1] = false; + U[lowest_open_1] = false; +#ifndef NDEBUG + if (trace >= 1) { + logger << "\033[1;38;5;33mremoving \033[36m" << label_vertex(lowest_open_1) << "\033[m" << std::endl; + } +#endif + } + if (player == 0 && lowest_open_0 == -2) break; // no more regions of player Even + if (player == 1 && lowest_open_1 == -2) break; // no more regions of player Odd + } + + return tangles != T or dominions != D; +} + + +void +DFTLSolver::run() +{ + tin = new std::vector[nodecount()]; + str = new int[nodecount()]; + + Z.resize(nodecount()); + S0.resize(nodecount()); + S1.resize(nodecount()); + G = disabled; + G.flip(); + CurG.resize(nodecount()); + V.resize(nodecount()); + W.resize(nodecount()); + + Parity.resize(nodecount()); + for (int v=0; v tout; // for each tangle + std::vector *tin; // for each normal vertex + std::vector tv; // the tangle (vertex-strategy pairs) + std::vector tpr; // priority of a tangle + + uintqueue Q; // main queue when attracting vertices + std::vector dom_vector_0; // stores the solved vertices (in dominions) + std::vector dom_vector_1; // stores the solved vertices (in dominions) + + int *str; // stores currently assigned strategy of each vertex + + uintqueue pea_vS; // vS + uintqueue pea_iS; // iS + uintqueue pea_S; // S + unsigned int* pea_vidx; // rindex + bitset pea_root; // root + int pea_curidx; // index + + std::vector tangle; // stores the new tangle + uintqueue tangleto; // stores the vertices the tangle can escape to + bitset escapes; // which escapes we considered + + bitset Z; // current region (in sptl) + bitset G; // the unsolved game + bitset S0; // solved vertices (in dom_vector_0) + bitset S1; // solved vertices (in dom_vector_1) + bitset CurG; // in search + + bitset V; + bitset W; + + bitset Parity; // vertices of parity 0/1 + + inline void attractVertices(const int pl, int v, bitset &R, bitset &Z, bitset &G, const int max_prio); + bool attractTangle(const int t, const int pl, bitset &R, bitset &Z, bitset &G, const int max_prio); + inline void attractTangles(const int pl, int v, bitset &R, bitset &Z, bitset &G, const int max_prio); + + bool search(const int player); + void partition(bitset &R, int top, bitset &Even, bitset &Odd); + bool tl(bitset &V, bitset &R, const int player, int &lowest_open_0, int &lowest_open_1); + bool sptl(bitset &V, bitset &R, const int player, int &lowest_top); + bool extractTangles(int i, bitset &R, int *str); +}; + +class ODFTLSolver : public DFTLSolver +{ +public: + ODFTLSolver(Oink *oink, Game *game) : DFTLSolver(oink, game) { oneplayer = true; } + virtual ~ODFTLSolver() { } +}; + + + +} + +#endif diff --git a/src/solvers.cpp b/src/solvers.cpp index 807345a..8ee9143 100644 --- a/src/solvers.cpp +++ b/src/solvers.cpp @@ -39,6 +39,7 @@ #include "ppq.hpp" #include "ptl.hpp" #include "dtl.hpp" +#include "dftl.hpp" namespace pg { @@ -73,6 +74,8 @@ Solvers::Solvers() add("spptl", "single-player progressive tangle learning", 0, [] (Oink* oink, Game* game) { return new SPPTLSolver(oink, game); }); add("dtl", "distance tangle learning", 0, [] (Oink* oink, Game* game) { return new DTLSolver(oink, game); }); add("idtl", "interleaved distance tangle learning", 0, [] (Oink* oink, Game* game) { return new IDTLSolver(oink, game); }); + add("dftl", "distraction-free tangle learning", 0, [] (Oink* oink, Game* game) { return new DFTLSolver(oink, game); }); + add("odftl", "one-player distraction-free tangle learning", 0, [] (Oink* oink, Game* game) { return new ODFTLSolver(oink, game); }); add("rtl", "recursive tangle learning", 0, [] (Oink* oink, Game* game) { return new RTLSolver(oink, game); }); add("ortl", "one-sided recursive tangle learning", 0, [] (Oink* oink, Game* game) { return new ORTLSolver(oink, game); }); add("tl", "tangle learning", 0, [] (Oink* oink, Game* game) { return new TLSolver(oink, game); }); diff --git a/src/tools/counter_odftl.cpp b/src/tools/counter_odftl.cpp new file mode 100644 index 0000000..1a4b088 --- /dev/null +++ b/src/tools/counter_odftl.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2021 Tom van Dijk, Johannes Kepler University Linz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +using namespace pg; + +int +main(int argc, char** argv) +{ + if (argc < 2) { + std::cout << "Syntax: " << argv[0] << " N" << std::endl; + return -1; + } + + const int n = std::stoi(argv[1]); // index of game + const int piv = 2+2*n; // pivot priority + + Game game(8 + 6*n); + + game.vec_init(); + + game.init_vertex(0, piv+6, 1); // top vertex of dominion + game.init_vertex(1, 0, 1); // first [distracted] tangle + game.init_vertex(2, piv+5, 1); // barrier + game.init_vertex(3, 2, 1); // second [distracted] tangle + game.init_vertex(4, 2, 1); // second [distracted] tangle "self-loop" assist + game.vec_add_edge(0, 3); // top vertex -> second tangle + game.vec_add_edge(1, 0); // first tangle -> top vertex + game.vec_add_edge(2, 1); // barrier -> first tangle + game.vec_add_edge(3, 2); // second tangle -> barrier + game.vec_add_edge(3, 4); // second tangle -> self-loop assist + game.vec_add_edge(4, 3); // self-loop assist -> second tangle + + game.init_vertex(5, piv+2, 0); // extra distracted vertex connected to first tangle + game.init_vertex(6, piv+4, 0); // distraction + game.init_vertex(7, piv+5, 0); // dominant + game.vec_add_edge(1, 5); // first tangle -> distracted vertex + game.vec_add_edge(5, 1); // distracted vertex -> first tangle + game.vec_add_edge(5, 6); // distracted vertex -> distraction + game.vec_add_edge(6, 7); // distraction -> dominant + game.vec_add_edge(7, 5); // dominant -> distracted vertex + + for (int i=0; i distracted vertex 1 + game.vec_add_edge(c+0, 1); // distracted vertex 1 -> first tangle + game.vec_add_edge(3, c+1); // second tangle -> distracted vertex 2 + game.vec_add_edge(c+1, 3); // distracted vertex 2 -> second tangle + game.vec_add_edge(c+0, c+2); // distracted vertex 1 -> distraction + game.vec_add_edge(c+1, c+2); // distracted vertex 2 -> distraction + game.vec_add_edge(c+2, c+4); // distraction -> dominant + game.vec_add_edge(c+3, c+1); // dominant -> distracted vertex 2 + game.vec_add_edge(c+4, c+5); // tangle + game.vec_add_edge(c+5, c+4); // tangle + game.vec_add_edge(c+4, c+3); // dominant -> distracted vertex 2 + } + + game.vec_finish(); + + //game.sort(); // optional sort vertices + //game.renumber(); // optional renumber vertices for tight priorities + + game.write_pgsolver(std::cout); +}