aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Harley2019-04-12 13:31:55 +0100
committerTom Harley2019-04-12 13:31:55 +0100
commit2baffcd0fbe27e29b3c10f46506087014aaaa7e0 (patch)
treec166e5f8ef79778ef6d9aa788b65986f33127ef1
parentAAAAAA (diff)
downloadmetasol-master.tar.gz
metasol-master.zip

This is the end

HEADmaster
-rw-r--r--README.md89
-rw-r--r--centre.cpp101
-rw-r--r--metasol.cpp22
-rw-r--r--metasol.hpp2
-rw-r--r--rules/accordion6
-rw-r--r--rules/alpha-star8
-rw-r--r--rules/delta-star5
-rw-r--r--rules/gaps-one-deal10
-rwxr-xr-xrules/klondike2
-rw-r--r--rules/scotch-patience7
-rw-r--r--rules/spiderette13
-rw-r--r--rules/trigon15
-rw-r--r--settings.json10
13 files changed, 278 insertions, 12 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..abc217b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,89 @@
+# `centre` - A General Solitaire Solver that Respects Hidden Information
+
+`centre` has been developed to showcase the abilities of `metasol`: an API for
+wrapping thoughtful solvers (ones that look at face-down cards) to create
+solvers that do not require any hidden information. The thoughtful solver used
+by `centre` is _Solvitaire_, which can solve many different solitaire variants,
+and as such `centre` is also highly flexible.
+
+## Getting `centre` onto Your System
+
+### Dependencies
+
+`centre` contains all of its dependencies, however _Solvitaire_ requires the C++
+Boost libraries installed on the system.
+
+`centre`'s dependencies can be built by running `make init-submodules`.
+
+### Building
+
+Building should be as simple as running `make`.
+
+## Usage
+
+The most basic usage of `centre` is to run it like so:
+
+```
+centre rules/klondike
+```
+
+This will run a randomly-generated game of _Klondike_ solitaire. Options can be
+passed after the rule file is specified - the list of possible options is
+viewable using `centre --help`.
+
+### Rule Files
+
+The rule files accepted by `centre` are simple JSON files that accept the
+following options:
+
+|Option | Meaning|
+|-------|--------|
+|`tableau size` | The number of piles in the tableau.|
+|`deck count` | The number of decks used.|
+|`max rank` | The highest rank of card used in the game. The usual is 13 (up to kings).|
+|`hole present` | Whether or not a hole is featured.|
+|`hole build loops` | Whether or not aces can be placed on kings in the hole, and vice versa.|
+|`foundations present` | Whether or not foundations are featured. The number of foundations, if enabled, is always equal to `4 * deck count`.|
+|`foundations removable` | Once a card has been placed in the foundations, whether or not it can be moved again.|
+|`foundations accept only complete piles` | Whether or not complete piles are the only way to move cards to the foundations.|
+|`diagonal deal` | Whether or not a diagonal deal (i.e. the deal used in _Klondike_) is used when laying out the tableau.|
+|`number of cells` | The number of cells used in the game.|
+|`cells pre-filled` | The number of cells that start play containing a card.|
+|`cards in stock` | The number of cards in the stock at the start of play.|
+|`stock deal count` | The number of cards removed from the stock when dealing from it.|
+|`stock redeal` | Whether or not the stock can be reused once it has been entirely dealt.|
+|`cards in reserve`| The number of cards placed in the reserve.|
+|`reserve stacked`| Whether or not the reserve is treated as a pile.|
+|`cards in sequence`| The number of cards in the sequence.|
+|`sequence fixed suit`| Whether or not a sequence uses a specific suit.|
+|`accordion size`| The number of cards in the accordion.|
+|`build policy`| The rule governing the movement of single cards across tableau piles.|
+|`spaces policy`| The rule governing the movement of single cards into empty tableau piles.|
+|`move built group`| The conditions for moving a group of cards.|
+|`group build policy`| The rule governing the movement of a group of cards across tableau piles.|
+|`foundations initialised`| Which foundations, if any, start play containing a card.|
+|`stock deal type`| Where cards from the stock go.|
+|`face up cards`| What cards are face-up when play begins.|
+|`sequence direction`| The direction in which cards are placed on the sequence.|
+|`sequence build policy`| The rule governing how cards are moved within the sequence.|
+|`accordion moves`| An array of legal accordion moves.|
+|`accordion policies`| An array of policies for the above moves.|
+
+### Settings
+
+Some settings can be passed to `centre` via the command line. Additionally,
+settings can be specified in a JSON file.
+
+|Name in File | Name on CLI | Meaning |
+|-------------|----------------------|---------|
+|`max concurrent threads` | | The maximum number of threads `centre` will have working at once.|
+|`max concurrent games` | | The maximum number of games `centre` will try to run in parallel.|
+|`solveseed`|`--solveseed`|The seed used by `centre` when making decisions.|
+| |`--dealseed`| The seed used by `centre` when generating a game-state.|
+|`run forever` | `--forever` | Runs `centre` indefinitely, on as many deals as possible.|
+|`run timeout` | `--limittime` | The maximum time a _Solvitaire_ instance may run.|
+|`run node limit` | `--limitnodes` | The maximum number of nodes a _Solvitaire_ instance may use.|
+|`initial vote count`| | The number of voters `centre` usually uses.|
+|`agree ratio` | | The percentage of votes that must agree for the move to be made.|
+|`vote increase step` | | The number of voters to add in the case of a split vote.|
+|`max vote count` | | The maximum number of voters `centre` will use.|
diff --git a/centre.cpp b/centre.cpp
index 6c98e88..b55ced8 100644
--- a/centre.cpp
+++ b/centre.cpp
@@ -12,6 +12,11 @@
#include "debug.h"
typedef struct {
+ int count;
+ sol_rules::direction direction;
+} accordion_move;
+
+typedef struct {
unsigned run_cache_size;
uint64_t run_timeout;
uint64_t run_node_limit;
@@ -27,8 +32,8 @@ typedef struct {
sol_rules::direction sequence_direction;
sol_rules::build_policy sequence_build_policy;
bool sequence_fixed_suit;
- std::vector<std::pair<sol_rules::direction, uint8_t>> accordion_moves;
- std::vector<sol_rules::accordion_policy> accordion_policy;
+ std::vector<accordion_move> accordion_moves;
+ std::vector<sol_rules::accordion_policy> accordion_policies;
} user_data;
static const char USAGE[] =
@@ -201,7 +206,10 @@ sol_rules::built_group_type str_built_group(char *str) {
{ "whole pile", sol_rules::built_group_type::WHOLE_PILE },
{ "maximal group only", sol_rules::built_group_type::MAXIMAL_GROUP },
- { "maximal", sol_rules::built_group_type::MAXIMAL_GROUP }
+ { "maximal", sol_rules::built_group_type::MAXIMAL_GROUP },
+
+ { "partial if card above buildable",
+ sol_rules::built_group_type::PARTIAL_IF_CARD_ABOVE_BUILDABLE },
};
lower(str);
@@ -291,10 +299,10 @@ sol_rules convert_rules(ms_rules *sr, user_data *d) {
for (auto &p : d->accordion_moves) {
r.accordion_moves.push_back(std::pair<sol_rules::direction, uint8_t>
- (p.first, p.second));
+ (p.direction, p.count));
}
- for (auto &x : d->accordion_policy) {
+ for (auto &x : d->accordion_policies) {
r.accordion_pol.push_back(x);
}
@@ -980,6 +988,61 @@ void json_settings(ms_settings *s, user_data *d, const char *filename) {
free(tokens);
}
+int assign_accordion_moves(jsmntok_t *key, const char *name,
+ std::vector<accordion_move> *rule, char *js) {
+ long length = key->end - key->start;
+ char *key_str = &js[key->start];
+ if (!strncmp(key_str, name, length)) {
+ jsmntok_t *val = next_tok(key);
+ assert(val->type == JSMN_ARRAY);
+ rule->clear();
+
+ jsmntok_t *t = val;
+ for (int i = 0; i < val->size; ++i) {
+ t = next_tok(t);
+ assert(t->type == JSMN_STRING);
+ accordion_move am;
+ am.count = js[t->start+1] - '0';
+
+ char dir = js[t->start];
+ assert(dir == 'L' || dir == 'R');
+ am.direction = dir == 'L' ?
+ sol_rules::direction::LEFT : sol_rules::direction::RIGHT;
+ rule->push_back(am);
+ }
+
+ return 2 + val->size;
+ } else {
+ return 0;
+ }
+}
+
+int assign_accordion_policies(jsmntok_t *key, const char *name,
+ std::vector<sol_rules::accordion_policy> *rule, char *js) {
+ long length = key->end - key->start;
+ char *key_str = &js[key->start];
+ if (!strncmp(key_str, name, length)) {
+ jsmntok_t *val = next_tok(key);
+ assert(val->type == JSMN_ARRAY);
+ rule->clear();
+
+ jsmntok_t *t = val;
+ for (int i = 0; i < val->size; ++i) {
+ t = next_tok(t);
+ assert(t->type == JSMN_STRING);
+
+ char buf[50];
+ strncpy(&buf[0], &js[t->start], t->end - t->start);
+ buf[t->end - t->start] = '\0';
+ rule->push_back(str_accordion_policy(&buf[0]));
+ }
+
+ return 2 + val->size;
+ } else {
+ return 0;
+ }
+}
+
ms_rules json_rules(const char *filename, ms_settings *s) {
user_data *d = (user_data *) s->user_data;
@@ -1094,6 +1157,9 @@ ms_rules json_rules(const char *filename, ms_settings *s) {
arp("stock-deal-count", NUMBER_TYPE, &r.stock_deal_count);
arp("stock_deal_count", NUMBER_TYPE, &r.stock_deal_count);
+ arp("stock redeal", BOOL_TYPE, &r.stock_redeal);
+ arp("stock-redeal", BOOL_TYPE, &r.stock_redeal);
+ arp("stock_redeal", BOOL_TYPE, &r.stock_redeal);
arp("stock redeal allowed", BOOL_TYPE, &r.stock_redeal);
arp("stock-redeal-allowed", BOOL_TYPE, &r.stock_redeal);
arp("stock_redeal_allowed", BOOL_TYPE, &r.stock_redeal);
@@ -1114,6 +1180,10 @@ ms_rules json_rules(const char *filename, ms_settings *s) {
arp("sequence-fixed-suit", BOOL_TYPE, &d->sequence_fixed_suit);
arp("sequence_fixed_suit", BOOL_TYPE, &d->sequence_fixed_suit);
+ arp("accordion size", NUMBER_TYPE, &r.accordion_size);
+ arp("accordion-size", NUMBER_TYPE, &r.accordion_size);
+ arp("accordion_size", NUMBER_TYPE, &r.accordion_size);
+
/*
* FIXME
* Doing some really sketchy casting here, but it should work:
@@ -1184,6 +1254,26 @@ ms_rules json_rules(const char *filename, ms_settings *s) {
are("sequence_build_policy", str_build_policy,
&d->sequence_build_policy);
+ x = assign_accordion_moves(tok, "accordion moves",
+ &d->accordion_moves, buf);
+ if (x) break;
+ x = assign_accordion_moves(tok, "accordion-moves",
+ &d->accordion_moves, buf);
+ if (x) break;
+ x = assign_accordion_moves(tok, "accordion_moves",
+ &d->accordion_moves, buf);
+ if (x) break;
+
+ x = assign_accordion_policies(tok, "accordion build policies",
+ &d->accordion_policies, buf);
+ if (x) break;
+ x = assign_accordion_policies(tok, "accordion-build-policies",
+ &d->accordion_policies, buf);
+ if (x) break;
+ x = assign_accordion_policies(tok, "accordion_build_policies",
+ &d->accordion_policies, buf);
+ if (x) break;
+
x = assign_foundations_base(tok, &r, buf);
if (x) {
break;
@@ -1191,6 +1281,7 @@ ms_rules json_rules(const char *filename, ms_settings *s) {
debug("%s\n", &buf[tok->start]);
assert(false);
+
} default: {
assert(false);
}
diff --git a/metasol.cpp b/metasol.cpp
index 89dec14..05a4259 100644
--- a/metasol.cpp
+++ b/metasol.cpp
@@ -242,10 +242,11 @@ void add_hidden(ms_card *c, std::vector<ms_card *> &cs) {
* Fills a vector with pointers to all hidden cards in the given game state.
*/
int get_hidden_cards(ms_game_state *gs, std::vector<ms_card *> &cs) {
- // TODO: include cells, accordion
- for (auto &c : gs->stock) add_hidden(&c, cs);
- for (auto &c : gs->waste) add_hidden(&c, cs);
- for (auto &c : gs->reserve) add_hidden(&c, cs);
+ // TODO: include cells
+ for (auto &c : gs->stock) add_hidden(&c, cs);
+ for (auto &c : gs->waste) add_hidden(&c, cs);
+ for (auto &c : gs->reserve) add_hidden(&c, cs);
+ for (auto &c : gs->accordion) add_hidden(&c, cs);
for (ms_card_pile &p : gs->foundations)
for (ms_card &c : p)
@@ -275,7 +276,7 @@ unsigned total_pile_count(ms_rules *r) {
+ (r->reserve_size > 0 ? 1 : 0)
;
- // TODO: I'm not sure how the sequence, or accordion work;
+ // TODO: I'm not sure how the sequence works;
// research required!
}
@@ -1007,7 +1008,16 @@ ms_game_state random_game_state(long user_seed, ms_rules *r) {
gs.reserve.back().hidden = false;
}
- // TODO: cells, accordion
+ // TODO: cells
+
+ if (r->accordion_size) {
+ for (unsigned i = 0; i < r->accordion_size; ++i) {
+ ms_card c = deck.back();
+ c.hidden = false;
+ deck.pop_back();
+ gs.accordion.push_back(c);
+ }
+ }
if (r->tableau_size) {
gs.tableau.resize(r->tableau_size);
diff --git a/metasol.hpp b/metasol.hpp
index 5216fa9..57c6dfb 100644
--- a/metasol.hpp
+++ b/metasol.hpp
@@ -14,6 +14,7 @@
#ifndef METASOL_H
#define METASOL_H
+#include <list>
#include <vector>
#include <random>
@@ -363,6 +364,7 @@ typedef struct {
ms_card_pile hole;
std::vector<ms_card_pile> cells;
ms_card_pile reserve;
+ std::list<ms_card> accordion;
/**
* The seed used for random generation of the game-state.
diff --git a/rules/accordion b/rules/accordion
new file mode 100644
index 0000000..ac15ff0
--- /dev/null
+++ b/rules/accordion
@@ -0,0 +1,6 @@
+foundations-present: false
+tableau-size: 0
+
+accordion-size: 52
+accordion-moves: [ "L1", "L3" ]
+accordion-build-policies: [ "same-suit", "same-suit" ]
diff --git a/rules/alpha-star b/rules/alpha-star
new file mode 100644
index 0000000..3275387
--- /dev/null
+++ b/rules/alpha-star
@@ -0,0 +1,8 @@
+#!centre
+
+tableau-size: 12
+build-policy: same-suit
+group-build-policy: same-suit
+move-built-group: yes
+
+foundations-initialised: all
diff --git a/rules/delta-star b/rules/delta-star
new file mode 100644
index 0000000..61b7148
--- /dev/null
+++ b/rules/delta-star
@@ -0,0 +1,5 @@
+tableau-size: 12
+build-policy: same-suit
+group-build-policy: same-suit
+
+foundations-initialised: all
diff --git a/rules/gaps-one-deal b/rules/gaps-one-deal
new file mode 100644
index 0000000..88efb01
--- /dev/null
+++ b/rules/gaps-one-deal
@@ -0,0 +1,10 @@
+#!centre
+
+foundations-present: false
+
+tableau-size: 0
+
+sequence-build-policy: same-suit
+sequence-size: 4
+sequence-direction: left
+sequence-fixed-suit: false
diff --git a/rules/klondike b/rules/klondike
index 18510de..9107fe1 100755
--- a/rules/klondike
+++ b/rules/klondike
@@ -3,7 +3,7 @@
tableau-size: 7
build-policy: red-black
spaces-policy: kings
-move-built-group: yes
+move-built-group: "partial if card above buildable"
group-build-policy: red-black
diagonal-deal: true
diff --git a/rules/scotch-patience b/rules/scotch-patience
new file mode 100644
index 0000000..05a3ce8
--- /dev/null
+++ b/rules/scotch-patience
@@ -0,0 +1,7 @@
+#!centre
+
+tableau-size: 18
+build-policy: red-black
+spaces-policy: no-build
+
+foundations-removable: true
diff --git a/rules/spiderette b/rules/spiderette
new file mode 100644
index 0000000..e81fa36
--- /dev/null
+++ b/rules/spiderette
@@ -0,0 +1,13 @@
+#!centre
+
+tableau-size: 7
+build-policy: any-suit
+move-built-group: yes
+group-build-policy: same-suit
+diagonal-deal: true
+face-up-policy: top
+
+foundations-only-complete-pile-moves: true
+
+cards-in-stock: 24
+stock-deal-type: stock-to-tableau
diff --git a/rules/trigon b/rules/trigon
new file mode 100644
index 0000000..42930ab
--- /dev/null
+++ b/rules/trigon
@@ -0,0 +1,15 @@
+#!centre
+
+tableau-size: 7
+build-policy: same-suit
+group-build-policy: same-suit
+move-built-group: yes
+spaces-policy: kings
+diagonal-deal: true
+face-up-policy: top
+
+foundations-removable: false
+
+cards-in-stock: 24
+stock-deal-count: 1
+stock-redeal: true
diff --git a/settings.json b/settings.json
new file mode 100644
index 0000000..10b2a40
--- /dev/null
+++ b/settings.json
@@ -0,0 +1,10 @@
+{
+ "max concurrent threads": 20,
+ "max concurrent games": 5,
+ "solve seed": 10,
+ "initial vote count": 10,
+ "max vote count": 1000,
+ "vote increase step": 0,
+ "vote increase magnitude": 5,
+ "agree ratio": 80
+}