#optimization #18xx #graphics

navig18xx

Construct 18xx tiles and maps, and find optimal route combinations

1 unstable release

0.1.0 Oct 8, 2021

#8 in #optimisation


Used in rusty-train

MIT/Apache

1MB
18K SLoC

Overview

A crate for working with 18xx tiles and maps, and searching for train routes with optimal revenue.

Current status

The following features are implemented:

  • Ability to define most 18xx tiles.
  • Drawing tiles on-screen and saving images to disk.
  • (De)serialising tile descriptions.
  • Placing tokens in token spaces.
  • Defining and manipulating 18xx game maps.
  • Selecting trains to operate routes for a company.
  • Selecting route bonuses for a company.
  • Searching maps for optimal pairings of trains to routes.

Supported games

Maps, tiles, and trains for the following games are implemented:

  • 1830: Railways and Robber Barons
  • 1861: The Railways of the Russian Empire
  • 1867: The Railways of Canada

Defining tiles

Use the Tile data structure. This uses the Cairo bindings provided by the Gtk-rs project.

use navig18xx::prelude::*;

// Define the basic tile geometry.
let hex_max_diameter = 125.0;
let hex = Hex::new(hex_max_diameter);

// Create a tile that contains two track segments and a dit.
let tile = Tile::new(
    HexColour::Yellow,
    "3",
    vec![
        Track::hard_l(HexFace::Bottom)
            .with_span(0.0, 0.5)
            .with_dit(TrackEnd::End, 10, DitShape::Bar),
        Track::hard_l(HexFace::Bottom).with_span(0.5, 1.0),
    ],
    vec![],
    &hex,
    );

// Save this tile to a JSON file.
let pretty_json = true;
write_tile("tile_3.json", &tile, pretty_json);
tile.save_png(&hex, "tile_3.png")
    .expect("Could not save tile as a PNG");

More complex tiles, with multiple token spaces and overlapping tracks, can be defined in the same way. For example, here are definitions of tiles 45 and X5:

#
#
let tile_45 = Tile::new(
    HexColour::Brown,
    "45",
    vec![
        Track::gentle_l(HexFace::UpperLeft),
        Track::hard_r(HexFace::Top),
        Track::gentle_r(HexFace::Bottom),
        Track::straight(HexFace::Bottom),
    ],
    vec![],
    &hex,
);
let tile_x5 = Tile::new(
    HexColour::Brown,
    "X5",
    vec![
        Track::straight(HexFace::Top).with_span(0.0, 0.1),
        Track::straight(HexFace::Top)
            .with_span(0.1, 1.0)
            .with_clip(0.3625, 0.75),
        Track::mid(HexFace::UpperLeft),
        Track::mid(HexFace::LowerLeft),
        Track::mid(HexFace::LowerRight),
        Track::mid(HexFace::UpperRight),
    ],
    vec![
        City::single_at_face(70, &HexFace::Top),
        City::double(70).in_dir(Direction::S, 0.1),
    ],
    &hex,
)
.label(Label::City("M".to_string()), HexCorner::BottomLeft)
.label(Label::Revenue(0), HexCorner::Left.to_centre(0.1));

tile_x5.save_png(&hex, "tile_x5.png")
    .expect("Could not save tile X5 as a PNG");
tile_x5.save_svg(&hex, "tile_x5.svg")
    .expect("Could not save tile X5 as an SVG");
tile_x5.save_pdf(&hex, "tile_x5.pdf")
    .expect("Could not save tile X5 as a PDF");

Dependencies

~8–12MB
~244K SLoC