/godip

A dippy adjudicator in Go.

Primary LanguageGo

godip

A DATC compliant dippy judge in Go.

Tested against over 5000 real games played by humans.

For autogenerated documentation, see http://godoc.org/github.com/zond/godip.

To run the DATC test cases:

cd classical
go test

To run the tests against the real human played games:

cd classical/droidippy/games
tar xvf games.tar.bz2
cd ..
go test

The real game tests will likely die from timeout, to continue testing where it left off:

cd classical/droidippy
env SKIP=game_xxxx.txt go test

Web service

http://godip-adjudication.appspot.com/ hosts a free public adjudicator based on godip.

GET http://godip-adjudication.appspot.com/ lists JSON for all supported variants.

GET http://godip-adjudication.appspot.com/{variant name} lists JSON for a starting map for the named variant.

POST http://godip-adjudication.appspot.com/{variant name} expects the body to be JSON matching a state previously provided by the corresponding GET URL but with populated orders, and returns the next state JSON.

See https://github.com/zond/godip/tree/master/gae for exact implementation details.

Variant support

Currently only Standard diplomacy and Fleet Rome are supported.

Many variants would, however, be simple to implement in godip, or using godip.

These are the truths that should be valid for such a variant to be godip compatible in a simple manner:

  • There is a Map with Provinces.
  • Each Province can contain at most one Unit.
  • Each Province can contain at most one SupplyCenter.
  • Each Province has one or more SubProvinces.
  • Each SubProvince has attributes, such as Sea, Land or Coast.
  • Each SubProvince has connections to other SubProvinces.
  • There are Units with UnitType and Nation.
  • Each unit is in one SubProvince.
  • There are SupplyCenters with Nation.
  • Each SupplyCenter is in one Province.
  • There are Phases with Year, Season and PhaseType.
  • Orders can vary greatly:
  • They can be valid only for certain Years, PhaseTypes or Seasons.
  • They can be valid only for certain UnitTypes.
  • They can be valid only for certain Map environments.
  • They can be valid only when certain other Orders are valid.
  • They can be valid only when certain Units are present.

Map format

Maps are svg files and can be created with a combination of the free tool Inkscape and your favourite text editor. The file should contain a pattern with id "stripes", which can be used by the client to highlight regions that the player can select. The file should have at least the following layers in it:

  • The background (bottom layer): This should contain regions in the colour they should be when not owned.
  • Provinces with id="provinces": This should be a hidden layer (style="display:none") containing each province. When a province is owned then it will be revealed and coloured appropriately. Each province should have its id set to the appropriate abbreviation (e.g. in the classical game the "London" region has the id "lon").
  • Supply centers with id="supply-centers": This is a layer of markers for supply centers. Each supply center should have the id set to the province name concatenated with "Center" (e.g. the supply center for "London" has the id "lonCenter"). These markers are used to position units and when drawing order symbols on the map.
  • Province centers with id="province-centers": This is a similar layer of markers, but for the regions which aren't supply centers. There should also be a center here for any subprovince (e.g. St. Petersberg North Coast has a center with id "stp/ncCenter"). The markers should follow the same naming convention as the supply center layer, but each marker should contain fill:none;stroke:none; in its style attribute so that it isn't displayed.
  • Highlights layer with id="highlights": This is used to highlight selectable regions.
  • Names layer with id="names": A layer containing text so the player knows what each region is called.
  • Units layer with id="units": This will be empty in the map file.
  • Orders layer with id="orders": This will also be empty in the map file.

Currently the script to handle map manipulation does not cope well with transform attributes. To remove these attributes from the svg use one of the methods on this page.

To prepare the svg file for use by go, the tool go-bindata can be used. For example the command go-bindata -pkg variants ./svg will convert all assets in the svg directory into a script in the package "variants".

Algorithm

Heavily inspired by The Math of Adjudication by Lucas Kruijswijk.

Res(x) is https://github.com/zond/godip/blob/master/state/resolver.go.

Adj(x) is defined in each order type.

Example runs:

6.C.3. TEST CASE, A DISRUPTED THREE ARMY CIRCULAR MOVEMENT

Running 6.C.3
Res(bul) (deps [])
  Adj(bul)
    'bul Move con' vs 'ank Move con': 1
    H2HDisl(con)
    Not dislodged
    'ank Move con' vs 'bul Move con': 1
  bul: Failure: ErrBounce:ank
  No guessing, resolving bul
bul: Failure: ErrBounce:ank (deps [])
Res(ank) (deps [])
  Adj(ank)
    'ank Move con' vs 'bul Move con': 1
    H2HDisl(con)
    Not dislodged
    'bul Move con' vs 'ank Move con': 1
  ank: Failure: ErrBounce:bul
  No guessing, resolving ank
ank: Failure: ErrBounce:bul (deps [])
Res(con) (deps [])
  Adj(con)
    'con Move smy' vs 'smy Move ank': 1
    Esc(smy)
      Res(smy) (deps [])
        Adj(smy)
          'smy Move ank' vs 'ank Move con': 1
          Esc(ank)
            Res(ank) (deps [])
              Resolved
            ank: Failure: ErrBounce:bul (deps [])
          Failure: ErrBounce:bul
        smy: Failure: ErrBounce:ank
        No guessing, resolving smy
      smy: Failure: ErrBounce:ank (deps [])
    Failure: ErrBounce:ank
  con: Failure: ErrBounce:smy
  No guessing, resolving con
con: Failure: ErrBounce:smy (deps [])
Res(smy) (deps [])
  Resolved
smy: Failure: ErrBounce:ank (deps [])

6.F.16. TEST CASE, PANDIN'S PARADOX

Running 6.F.17 (Pandin's extended paradox)
Res(eng) (deps [])
  Adj(eng)
    Res(bel) (deps [])
      Adj(bel)
        Res(nth) (deps [])
          Adj(nth)
          nth: Success
          No guessing, resolving nth
        nth: Success (deps [])
        'bel Move eng' vs 'wal Move eng': 2
        Res(lon) (deps [])
          Adj(lon)
            Res(eng) (deps [])
              Already resolving eng, making negative guess
            eng: Failure: Negative guess (deps [eng])
            Res(eng) (deps [eng])
              Guessed
            eng: Failure: Negative guess (deps [eng])
            Res(eng) (deps [eng])
              Guessed
            eng: Failure: Negative guess (deps [eng])
            Res(eng) (deps [eng])
              Guessed
            eng: Failure: Negative guess (deps [eng])
            Res(bre) (deps [eng])
              Adj(bre)
                Res(eng) (deps [eng])
                  Guessed
                eng: Failure: Negative guess (deps [eng])
                Res(eng) (deps [eng])
                  Guessed
                eng: Failure: Negative guess (deps [eng])
              bre: Failure: ErrMissignConvoyPath
            bre: Failure: ErrMissignConvoyPath (deps [eng])
          lon: Success
          Made new guess, adding lon to deps
        lon: Success (deps [eng lon])
        H2HDisl(eng)
        Not dislodged
        'wal Move eng' vs 'bel Move eng': 2
      bel: Failure: ErrBounce:wal
      Made new guess, adding bel to deps
    bel: Failure: ErrBounce:wal (deps [eng lon bel])
    Res(wal) (deps [eng lon bel])
      Adj(wal)
        Res(lon) (deps [eng lon bel])
          Adj(lon)
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Failure: Negative guess (deps [eng lon bel])
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Failure: Negative guess (deps [eng lon bel])
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Failure: Negative guess (deps [eng lon bel])
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Failure: Negative guess (deps [eng lon bel])
            Res(bre) (deps [eng lon bel])
              Adj(bre)
                Res(eng) (deps [eng lon bel])
                  Guessed
                eng: Failure: Negative guess (deps [eng lon bel])
                Res(eng) (deps [eng lon bel])
                  Guessed
                eng: Failure: Negative guess (deps [eng lon bel])
              bre: Failure: ErrMissignConvoyPath
            bre: Failure: ErrMissignConvoyPath (deps [eng lon bel])
          lon: Success
        lon: Success (deps [eng lon bel])
        'wal Move eng' vs 'bel Move eng': 2
        Res(nth) (deps [eng lon bel])
          Resolved
        nth: Success (deps [eng lon bel])
        H2HDisl(eng)
        Not dislodged
        'bel Move eng' vs 'wal Move eng': 2
      wal: Failure: ErrBounce:bel
    wal: Failure: ErrBounce:bel (deps [eng lon bel])
  eng: Success
  Guess made for eng, changing guess to positive
  Adj(eng)
    Res(bel) (deps [eng lon bel])
      Adj(bel)
        Res(nth) (deps [eng lon bel])
          Resolved
        nth: Success (deps [eng lon bel])
        'bel Move eng' vs 'wal Move eng': 2
        Res(lon) (deps [eng lon bel])
          Adj(lon)
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Success (deps [eng lon bel])
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Success (deps [eng lon bel])
            lon Support [wal eng]: broken by: [bre]
          lon: Failure: ErrSupportBroken:bre
        lon: Failure: ErrSupportBroken:bre (deps [eng lon bel])
        'wal Move eng' vs 'bel Move eng': 1
        Res(nth) (deps [eng lon bel])
          Resolved
        nth: Success (deps [eng lon bel])
        'bel Move eng' vs 'eng Convoy [bre lon]': 2
        'eng Convoy [bre lon]': 1
        Res(nth) (deps [eng lon bel])
          Resolved
        nth: Success (deps [eng lon bel])
        'bel Move eng' vs 'wal Move eng': 2
        Res(lon) (deps [eng lon bel])
          Adj(lon)
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Success (deps [eng lon bel])
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Success (deps [eng lon bel])
            lon Support [wal eng]: broken by: [bre]
          lon: Failure: ErrSupportBroken:bre
        lon: Failure: ErrSupportBroken:bre (deps [eng lon bel])
        'wal Move eng' vs 'bel Move eng': 1
      bel: Success
    bel: Success (deps [eng lon bel])
    Res(wal) (deps [eng lon bel])
      Adj(wal)
        Res(lon) (deps [eng lon bel])
          Adj(lon)
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Success (deps [eng lon bel])
            Res(eng) (deps [eng lon bel])
              Guessed
            eng: Success (deps [eng lon bel])
            lon Support [wal eng]: broken by: [bre]
          lon: Failure: ErrSupportBroken:bre
        lon: Failure: ErrSupportBroken:bre (deps [eng lon bel])
        'wal Move eng' vs 'bel Move eng': 1
        Res(nth) (deps [eng lon bel])
          Resolved
        nth: Success (deps [eng lon bel])
        H2HDisl(eng)
        Not dislodged
        'bel Move eng' vs 'wal Move eng': 2
      wal: Failure: ErrBounce:bel
    wal: Failure: ErrBounce:bel (deps [eng lon bel])
  eng: Failure: ErrConvoyDislodged:bel
  Calling backup rule with [eng lon bel]
  Calling backup rule for [eng lon bel]
  Res(eng) (deps [])
    Resolved
  eng: Failure: ErrConvoyParadox (deps [])
  No guessing, resolving eng
eng: Failure: ErrConvoyParadox (deps [])
Res(nth) (deps [])
  Resolved
nth: Success (deps [])
Res(bre) (deps [])
  Adj(bre)
    Res(eng) (deps [])
      Resolved
    eng: Failure: ErrConvoyParadox (deps [])
    Res(eng) (deps [])
      Resolved
    eng: Failure: ErrConvoyParadox (deps [])
  bre: Failure: ErrMissignConvoyPath
  No guessing, resolving bre
bre: Failure: ErrMissignConvoyPath (deps [])
Res(wal) (deps [])
  Adj(wal)
    Res(lon) (deps [])
      Adj(lon)
        Res(eng) (deps [])
          Resolved
        eng: Failure: ErrConvoyParadox (deps [])
        Res(eng) (deps [])
          Resolved
        eng: Failure: ErrConvoyParadox (deps [])
        Res(eng) (deps [])
          Resolved
        eng: Failure: ErrConvoyParadox (deps [])
        Res(eng) (deps [])
          Resolved
        eng: Failure: ErrConvoyParadox (deps [])
        Res(bre) (deps [])
          Resolved
        bre: Failure: ErrMissignConvoyPath (deps [])
      lon: Success
      No guessing, resolving lon
    lon: Success (deps [])
    'wal Move eng' vs 'bel Move eng': 2
    Res(nth) (deps [])
      Resolved
    nth: Success (deps [])
    H2HDisl(eng)
    Not dislodged
    'bel Move eng' vs 'wal Move eng': 2
  wal: Failure: ErrBounce:bel
  No guessing, resolving wal
wal: Failure: ErrBounce:bel (deps [])
Res(lon) (deps [])
  Resolved
lon: Success (deps [])
Res(yor) (deps [])
  Adj(yor)
  yor: Success
  No guessing, resolving yor
yor: Success (deps [])
Res(bel) (deps [])
  Adj(bel)
    Res(nth) (deps [])
      Resolved
    nth: Success (deps [])
    'bel Move eng' vs 'wal Move eng': 2
    Res(lon) (deps [])
      Resolved
    lon: Success (deps [])
    H2HDisl(eng)
    Not dislodged
    'wal Move eng' vs 'bel Move eng': 2
  bel: Failure: ErrBounce:wal
  No guessing, resolving bel
bel: Failure: ErrBounce:wal (deps [])