Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add /tiles/test route to easily reference tiles from different titles and game states #9175

Merged
merged 4 commits into from
May 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require_relative 'models'
require_rel './lib'
require_rel './models'
require_relative 'lib/engine/test_tiles'

class Api < Roda
opts[:check_dynamic_arity] = false
Expand Down Expand Up @@ -132,7 +133,16 @@ class Api < Roda

r.on 'tiles' do
parts = request.path.split('/')
titles = parts.size == 4 ? parts[2].split(/[+ ]/) : []

titles =
if parts.size == 4
parts[2].split(/[+ ]/)
elsif parts[2] == 'test'
Engine::TestTiles::TEST_TILES.keys.compact
else
[]
end

render(titles: titles)
end

Expand Down
2 changes: 1 addition & 1 deletion assets/app/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def render_content
when /about/
h(View::About)
when /tiles/
h(View::TilesPage, route: @app_route)
h(View::TilesPage, route: @app_route, connection: @connection)
when /map/
h(View::MapPage, route: @app_route)
when /market/
Expand Down
43 changes: 39 additions & 4 deletions assets/app/view/tiles.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ class Tiles < Snabberb::Component
padding: '0 0.7rem 0 0.2rem',
},
}.freeze
BOTTOM_LINE_PROPS = {
style: {
height: '0.9rem',
padding: '0 0.7rem 0 0.2rem',
bottom: '3px',
position: 'absolute',
},
}.freeze
TEXT_PROPS = {
style: {
float: 'left',
Expand All @@ -39,29 +47,51 @@ def render_tile_blocks(
rotations: nil,
hex_coordinates: nil,
clickable: false,
extra_children: []
location_on_plain: false,
extra_children: [],
top_text: nil,
fixture_id: nil,
fixture_title: nil,
action: nil
)
block_props = {
style: {
width: "#{WIDTH * scale}px",
height: "#{HEIGHT * scale}px",
position: 'relative',
},
}

tile ||= Engine::Tile.for(name)

loc_name = location_name || tile.location_name if (tile.cities + tile.towns + tile.offboards).any?
loc_name = location_name || tile.location_name if !(tile.cities + tile.towns + tile.offboards).empty? || location_on_plain

rotations = [0] if tile.preprinted || !rotations

rotations.map do |rotation|
tile.rotate!(rotation)

unless setting_for(@hide_tile_names)
if setting_for(@hide_tile_names)
text = nil
elsif top_text
text = top_text
else
text = tile.preprinted ? '' : '#'
text += name
text += "-#{rotation}" unless rotations == [0]
end

text += "-#{rotation}" if !setting_for(@hide_tile_names) && rotations != [0]

bottom_text = ''
if fixture_id
bottom_text = fixture_id
href = "/fixture/#{fixture_title}/#{fixture_id}"
if action
bottom_text += " action=#{action}"
href += "?action=#{action}"
end
end

count = tile.unlimited ? '∞' : num.to_s

hex = Engine::Hex.new(hex_coordinates || 'A1',
Expand All @@ -88,6 +118,11 @@ def render_tile_blocks(
),
]),
]),
h(:div, BOTTOM_LINE_PROPS, [
h(:div, TEXT_PROPS, [
h(:a, { attrs: { href: href } }, bottom_text),
]),
]),
])
end
end
Expand Down
119 changes: 115 additions & 4 deletions assets/app/view/tiles_page.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# frozen_string_literal: true

require 'game_manager'
require 'lib/connection'
require 'engine/test_tiles'
require 'lib/params'
require 'view/tiles'
require_relative '../game_class_loader'

module View
class TilesPage < Tiles
include GameClassLoader
include GameManager

needs :route
needs :fixture_data, default: {}, store: true

ROUTE_FORMAT = %r{/tiles/([^/?]*)(?:/([^?]+))?}.freeze

Expand Down Expand Up @@ -51,6 +54,10 @@ def render

])

elsif dest == 'test'

render_test_tiles

elsif dest == 'custom'
location_name = Lib::Params['n']
color = Lib::Params['c'] || 'yellow'
Expand Down Expand Up @@ -112,7 +119,7 @@ def render
end
end

def render_individual_tile_from_game(game, hex_or_tile_id)
def render_individual_tile_from_game(game, hex_or_tile_id, scale: 3.0, **kwargs)
id, rotation = hex_or_tile_id.split('-')
rotations = rotation ? [rotation.to_i] : @rotations

Expand All @@ -132,9 +139,10 @@ def render_individual_tile_from_game(game, hex_or_tile_id)
layout: game.class::LAYOUT,
tile: tile,
location_name: tile.location_name || @location_name,
scale: 3.0,
scale: scale,
rotations: rotations,
hex_coordinates: hex_coordinates,
**kwargs,
)
end

Expand Down Expand Up @@ -246,5 +254,108 @@ def render_toggle_button
h(:'button.small', { on: { click: toggle } }, "Tile Names #{setting_for(@hide_tile_names) ? '❌' : '✅'}"),
])
end

def render_test_tiles
# see /lib/engine/test_tiles.rb
test_tiles = Engine::TestTiles::TEST_TILES

scale = 2.0

return h(:div, [h(:p, 'Loading...')]) unless @connection

rendered_test_tiles = []

test_tiles.each do |title, fixtures|
if title
game_class = load_game_class(title)
else
fixtures[nil][nil].each do |hex_or_tile, opts|
%i[flat pointy].each do |layout_|
rendered_test_tiles.concat(
render_tile_blocks(hex_or_tile, layout: layout_, scale: scale, rotations: @rotations, **opts)
)
end
end
next
end

fixtures.each do |fixture, actions|
if fixture
if @fixture_data[fixture]
actions.each do |action, hex_or_tiles|
kwargs = action ? { at_action: action } : {}

game = Engine::Game.load(@fixture_data[fixture], **kwargs)

hex_or_tiles.each do |hex_or_tile, opts|
hex_coordinates = hex_or_tile
tile = game.hex_by_id(hex_coordinates).tile

rendered_test_tiles.concat(
render_tile_blocks(
hex_coordinates,
layout: game_class::LAYOUT,
tile: tile,
location_name: tile.location_name,
location_on_plain: true,
scale: scale,
rotations: [tile.rotation],
hex_coordinates: hex_coordinates,
name_prefix: title,
top_text: "#{title}: #{hex_or_tile}",
fixture_id: fixture,
fixture_title: title,
action: action,
**opts,
)
)
end
end

elsif @connection
# load the fixture game data
@connection.get("/fixtures/#{title}/#{fixture}.json", '') do |data|
@fixture_data[fixture] = data
store(:fixture_data, @fixture_data, skip: false)
end

# render placeholder tiles which will be replaced once the
# appropriate fixture is loaded and processed
actions.each do |action, hex_or_tiles|
hex_or_tiles.each do |hex_or_tile, opts|
rendered_test_tiles.concat(
render_tile_blocks(
'blank',
layout: game_class::LAYOUT,
location_name: 'Loading Fixture...',
location_on_plain: true,
scale: scale,
name_prefix: title,
top_text: "#{title}: #{hex_or_tile}",
fixture_id: fixture,
fixture_title: title,
action: action,
**opts
)
)
end
end
end

else
players = Array.new(game_class::PLAYER_RANGE.max) { |n| "Player #{n + 1}" }
game = game_class.new(players)
actions[nil].each do |hex_or_tile, opts|
rendered_test_tiles.concat(
Array(render_individual_tile_from_game(game, hex_or_tile, scale: scale, top_text: "#{title}: #{hex_or_tile}",
**opts))
)
end
end
end
end

h('div#tiles', rendered_test_tiles)
end
end
end
80 changes: 80 additions & 0 deletions lib/engine/test_tiles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# frozen_string_literal: true

module Engine
module TestTiles
# each entry is a Hash containing:
# - tile / hex id
# - game title (optional)
# - fixture (optional, requires game title; if not given, the starting state
# of the hex/tile is used)
# - action id (optional, requires fixture; if not given, the fixture is
# processed to its conclusion)
# - other kwargs for View::Tiles#render_tile_blocks
TEST_TILES_HUMAN_READABLE = [
{ tile: '45' },

{ tile: 'H11', title: '1822PNW' },
{ tile: 'O8', title: '1822PNW' },
{ tile: 'I12', title: '1822PNW' },

# open: https://github.com/tobymao/18xx/issues/5981
{ tile: 'H22', title: '1828.Games' },

# open: https://github.com/tobymao/18xx/issues/8178
{ tile: 'H18', title: '1830', fixture: '26855', action: 385 },

{ tile: 'C15', title: '1846' },

# open: https://github.com/tobymao/18xx/issues/5167
{ tile: 'N11', title: '1856', fixture: 'hotseat005', action: 113 },

{ tile: 'L0', title: '1868 Wyoming' },
{ tile: 'WRC', title: '1868 Wyoming' },
{ tile: 'F12', title: '1868 Wyoming', fixture: '1868WY_5', action: 835 },
{ tile: 'L0', title: '1868 Wyoming', fixture: '1868WY_5', action: 835 },
{ tile: 'J12', title: '1868 Wyoming', fixture: '1868WY_5', action: 835 },
{ tile: 'J12', title: '1868 Wyoming', fixture: '1868WY_5' },

# open: https://github.com/tobymao/18xx/issues/4992
{ tile: 'I11', title: '1882', fixture: '5236', action: 303 },

# open: https://github.com/tobymao/18xx/issues/6604
{ tile: 'L41', title: '1888' },

# open: https://github.com/tobymao/18xx/issues/5153
{ tile: 'IR7', title: '18Ireland' },
{ tile: 'IR8', title: '18Ireland' },

# open: https://github.com/tobymao/18xx/issues/5673
{ tile: 'D19', title: '18Mag', fixture: 'hs_tfagolvf_76622' },
{ tile: 'I14', title: '18Mag', fixture: 'hs_tfagolvf_76622' },

# open: https://github.com/tobymao/18xx/issues/7765
{ tile: '470', title: '18MEX' },
{ tile: '475', title: '18MEX' },
{ tile: '479P', title: '18MEX' },
{ tile: '485P', title: '18MEX' },
{ tile: '486P', title: '18MEX' },
].freeze

# rearrange the above to a structure that can be more efficiently iterated
# over--each fixture only needs to be fetched once, and only needs to be
# processed to each unique action once
#
# defining with this structure directly would confusing to read; for generic
# tiles, all of the keys in the nested Hash would end up as `nil`
TEST_TILES =
TEST_TILES_HUMAN_READABLE.each_with_object({}) do |opts, test_tiles|
tile = opts.delete(:tile)
title = opts.delete(:title)
fixture = opts.delete(:fixture)
action = opts.delete(:action)

test_tiles[title] ||= {}
test_tiles[title][fixture] ||= {}
test_tiles[title][fixture][action] ||= []

test_tiles[title][fixture][action] << [tile, opts]
end.freeze
end
end
1 change: 0 additions & 1 deletion public/fixtures

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions public/fixtures/1868 Wyoming/1868WY_5.json

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions public/fixtures/18Mag/hs_tfagolvf_76622.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"status":"active","actions":[{"type":"bid","entity":1355,"entity_type":"player","id":1,"created_at":1647183951,"minor":"1","price":0},{"type":"bid","entity":10497,"entity_type":"player","id":2,"created_at":1647183984,"minor":"6","price":0},{"type":"bid","entity":1423,"entity_type":"player","id":3,"created_at":1647184001,"minor":"3","price":0},{"type":"bid","entity":1443,"entity_type":"player","id":4,"created_at":1647184054,"corporation":"SKEV","price":0},{"type":"bid","entity":3510,"entity_type":"player","id":5,"created_at":1647184101,"minor":"12","price":0},{"type":"bid","entity":10497,"entity_type":"player","id":6,"created_at":1647184116,"minor":"2","price":0},{"type":"bid","entity":1423,"entity_type":"player","id":7,"created_at":1647184148,"corporation":"SKEV","price":0},{"type":"bid","entity":1443,"entity_type":"player","id":8,"created_at":1647184181,"minor":"4","price":0},{"type":"bid","entity":3510,"entity_type":"player","id":9,"created_at":1647184188,"corporation":"SNW","price":0},{"type":"bid","entity":1355,"entity_type":"player","id":10,"created_at":1647184217,"minor":"7","price":0},{"type":"bid","entity":1423,"entity_type":"player","id":11,"created_at":1647184273,"minor":"5","price":0},{"type":"bid","entity":1443,"entity_type":"player","id":12,"created_at":1647184287,"corporation":"LdStEG","price":0},{"type":"bid","entity":3510,"entity_type":"player","id":13,"created_at":1647184315,"minor":"10","price":0},{"type":"bid","entity":1355,"entity_type":"player","id":14,"created_at":1647184337,"corporation":"LdStEG","price":0},{"type":"bid","entity":10497,"entity_type":"player","id":15,"created_at":1647184346,"corporation":"SNW","price":0},{"type":"bid","entity":1443,"entity_type":"player","id":16,"created_at":1647184387,"minor":"9","price":0},{"type":"bid","entity":3510,"entity_type":"player","id":17,"created_at":1647184399,"corporation":"SIK","price":0},{"type":"bid","entity":1355,"entity_type":"player","id":18,"created_at":1647184495,"corporation":"SIK","price":0},{"type":"bid","entity":10497,"entity_type":"player","id":19,"created_at":1647184502,"corporation":"RABA","price":0},{"type":"bid","entity":1423,"entity_type":"player","id":20,"created_at":1647184546,"corporation":"G&C","price":0},{"hex":"D13","tile":"58-0","type":"lay_tile","entity":"1","rotation":0,"entity_type":"minor","id":21,"user":1355,"created_at":1647184614,"skip":true},{"hex":"E12","tile":"L33-0","type":"lay_tile","entity":"1","rotation":2,"entity_type":"minor","id":22,"user":1355,"created_at":1647184617,"skip":true},{"skip":true,"type":"undo","entity":"1","action_id":20,"entity_type":"minor","id":23,"user":1355,"created_at":1647184625},{"type":"lay_tile","entity":"1","entity_type":"minor","id":24,"created_at":1647184633,"hex":"E12","tile":"L33-0","rotation":2},{"type":"lay_tile","entity":"1","entity_type":"minor","id":25,"created_at":1647184636,"hex":"F13","tile":"6-0","rotation":0},{"type":"pass","entity":"1","entity_type":"minor","id":26,"created_at":1647184645},{"type":"pass","entity":"1","entity_type":"minor","id":27,"created_at":1647184655},{"type":"run_routes","entity":"1","entity_type":"minor","id":28,"created_at":1647184662,"routes":[{"train":"2-0","connections":[["F13","E12"]],"hexes":["E12","F13"],"revenue":50,"revenue_str":"E12-F13","subsidy":0,"nodes":["F13-0","E12-0"]}]},{"type":"pass","entity":"1","entity_type":"minor","id":29,"created_at":1647184669},{"hex":"D19","tile":"L32-0","type":"lay_tile","entity":"2","rotation":0,"entity_type":"minor","id":30,"user":10497,"created_at":1647184678,"skip":true},{"skip":true,"type":"undo","entity":"2","entity_type":"minor","id":31,"user":10497,"created_at":1647184692},{"hex":"D19","tile":"L32-0","type":"lay_tile","entity":"2","rotation":1,"entity_type":"minor","id":32,"user":10497,"created_at":1647184697,"skip":true},{"skip":true,"type":"undo","entity":"2","entity_type":"minor","id":33,"user":10497,"created_at":1647184701},{"type":"lay_tile","entity":"2","entity_type":"minor","id":34,"created_at":1647184715,"hex":"D19","tile":"L32-0","rotation":4},{"type":"undo","entity":"2","entity_type":"minor","id":35,"created_at":1683764194},{"type":"lay_tile","entity":"2","entity_type":"minor","id":36,"created_at":1683764197,"hex":"D19","tile":"L32-0","rotation":2},{"type":"pass","entity":"2","entity_type":"minor","id":37,"created_at":1683764204},{"type":"pass","entity":"2","entity_type":"minor","id":38,"created_at":1683764205},{"type":"pass","entity":"2","entity_type":"minor","id":39,"created_at":1683764206},{"type":"pass","entity":"3","entity_type":"minor","id":40,"created_at":1683764206},{"type":"pass","entity":"3","entity_type":"minor","id":41,"created_at":1683764207},{"type":"pass","entity":"3","entity_type":"minor","id":42,"created_at":1683764208},{"type":"pass","entity":"4","entity_type":"minor","id":43,"created_at":1683764210},{"type":"pass","entity":"4","entity_type":"minor","id":44,"created_at":1683764210},{"type":"pass","entity":"4","entity_type":"minor","id":45,"created_at":1683764211},{"type":"pass","entity":"5","entity_type":"minor","id":46,"created_at":1683764211},{"type":"pass","entity":"5","entity_type":"minor","id":47,"created_at":1683764212},{"type":"pass","entity":"5","entity_type":"minor","id":48,"created_at":1683764212},{"type":"pass","entity":"6","entity_type":"minor","id":49,"created_at":1683764213},{"type":"pass","entity":"6","entity_type":"minor","id":50,"created_at":1683764214},{"type":"pass","entity":"6","entity_type":"minor","id":51,"created_at":1683764214},{"type":"pass","entity":"7","entity_type":"minor","id":52,"created_at":1683764215},{"type":"pass","entity":"7","entity_type":"minor","id":53,"created_at":1683764215},{"type":"pass","entity":"7","entity_type":"minor","id":54,"created_at":1683764216},{"type":"lay_tile","entity":"9","entity_type":"minor","id":55,"created_at":1683764220,"hex":"I14","tile":"L32-1","rotation":1}, {"type": "end_game","entity": "9","entity_type": "minor","id": 56,"created_at": 1683774126}],"id":"hs_tfagolvf_76622","description":"Cloned from game 76622","user":{"id":0,"name":"You"},"players":[{"id":1355,"name":"hubbs"},{"id":10497,"name":"mnfuller"},{"id":1423,"name":"Breich"},{"id":1443,"name":"vexhawk"},{"id":3510,"name":"Harry"}],"max_players":5,"title":"18Mag","settings":{"seed":1382898850,"is_async":true,"unlisted":true,"auto_routing":true,"player_order":null,"optional_rules":[]},"user_settings":null,"turn":1,"round":"Operating Round","acting":[1443],"result":{"10497":140, "1355":155, "1423":145, "1443":135, "3510":140},"loaded":true,"created_at":"2023-05-10","updated_at":1683764220,"mode":"hotseat","manually_ended":null}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions spec/fixtures
2 changes: 1 addition & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
end
end

FIXTURES_DIR = File.join(File.dirname(__FILE__), 'fixtures')
FIXTURES_DIR = File.join(File.dirname(__FILE__), '..', 'public', 'fixtures')