initial commit
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
_output/*.png
|
||||
_output/*.pdf
|
||||
~$*
|
||||
.DS_Store
|
||||
._graphics_compute_touch
|
||||
layout.yml
|
||||
_graphics
|
||||
9
Makefile
Normal file
9
Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
all: deck.rb cards.xlsx ._graphics_compute_touch layout.yml config.yml
|
||||
rake
|
||||
|
||||
._graphics_compute_touch: graphics.svg
|
||||
python extract.py
|
||||
touch ._graphics_compute_touch
|
||||
|
||||
layout.yml: compute_layout.py
|
||||
python compute_layout.py
|
||||
73
RULES.md
Normal file
73
RULES.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Intro
|
||||
Le mandat du dernier Robo-Président arrive à son terme, la nation entière de Robonia s'agite pour élire un nouveau gouvernement. À la tête d'un gros groupe influent (Robo-mafia, Robo-conglomérat, Robo-illuminati...), vous voulez faire élire les représentants qui vous renverront le Robo-ascenseur le plus haut !
|
||||
|
||||
# Objectif
|
||||
Au cours de plusieurs élections (manches), vous devez accumuler le plus de points d'influence (PI) possible. À la fin de la partie, le joueur ayant le plus de points d'influence est déclaré vainqueur !
|
||||
|
||||
# Matériel
|
||||
X cartes
|
||||
- Y cartes candidat (Yc de chaque couleur)
|
||||
- Z cartes action
|
||||
1 plateau de sondage
|
||||
20 pions "point de vote"
|
||||
|
||||
# Mise en place
|
||||
(Avec image et flèches)
|
||||
- Placez le plateau au milieu de la table.
|
||||
- Placez la pioche au centre du plateau et laissez un espace pour la défausse.
|
||||
- Disposez un point de vote sur chaque zone de parti sur le plateau, et placez les pions restant au milieu.
|
||||
|
||||
Le joueur qui fait le mieux la danse du robot commence la partie.
|
||||
|
||||
# Comment jouer ?
|
||||
Une **partie** de Robolitik se déroule en **trois manches***, durant lesquelles les joueurs effectuent successivement des **tours**.
|
||||
|
||||
## Tour de jeu
|
||||
Le tour d'un joueur se déroule en 3 temps
|
||||
1. Le joueur **doit** piocher
|
||||
2. Le joueur **peut** jouer des cartes
|
||||
3. Le joueur **doit** défausser une carte
|
||||
|
||||
### Piocher
|
||||
Au début de son tour, le joueur a le choix entre :
|
||||
- Piocher les deux premières cartes de la pioche
|
||||
- Piocher la permière carte de la défausse
|
||||
|
||||
Si il ne reste pas suffisament de cartes dans la pioche, la défausse est mélangée pour former une nouvelle pioche.
|
||||
|
||||
### Jouer des cartes
|
||||
#### jouer des cartes candidat
|
||||
Le joueur
|
||||
|
||||
#### jouer des cartes action
|
||||
|
||||
### Défausser
|
||||
À la fin de son tour, le joueur défausse en plaçant une des cartes de sa main face visible sur la défausse. Si un joueur n'a plus de cartes, il n'est pas obligé d'en défausser une.
|
||||
|
||||
## Fin d'une manche
|
||||
|
||||
### Quand une manche se termine-t-elle ?
|
||||
Une manche peut se terminer de deux manières :
|
||||
- Un joueur n'a plus de cartes en main
|
||||
- La pioche a été épuisée à deux reprises
|
||||
|
||||
Dans les deux cas, la manche se termine immédiatement et on attribue les points d'influence pour la manche.
|
||||
|
||||
### Attribution des points d'une manche
|
||||
TODO
|
||||
|
||||
### Mise en place pour la prochaine manche
|
||||
Retirez 1 point de vote sur chaque zone de parti. Retirez tous les points de vote du ou des partis ayant gagné
|
||||
|
||||
# Fin de partie et victoire
|
||||
À la fin de la troisième manche, le joueur ayant le plus de points d'influence est déclaré vainqueur !
|
||||
|
||||
# FAQ???
|
||||
|
||||
# Index???
|
||||
(iconographie etc)
|
||||
|
||||
|
||||
|
||||
+ examples
|
||||
+ images
|
||||
7
Rakefile
Normal file
7
Rakefile
Normal file
@@ -0,0 +1,7 @@
|
||||
require 'squib'
|
||||
|
||||
task default: [:deck]
|
||||
|
||||
task :deck do
|
||||
load 'deck.rb'
|
||||
end
|
||||
1
_output/gitkeep.txt
Normal file
1
_output/gitkeep.txt
Normal file
@@ -0,0 +1 @@
|
||||
Keep this here so that Git knows to keep the _output directory on a fresh clone
|
||||
BIN
cards.xlsx
Normal file
BIN
cards.xlsx
Normal file
Binary file not shown.
119
compute_layout.py
Normal file
119
compute_layout.py
Normal file
@@ -0,0 +1,119 @@
|
||||
from typing import *
|
||||
|
||||
|
||||
# Card size info
|
||||
TOTAL_HEIGHT = 1125
|
||||
TOTAL_WIDTH = 825
|
||||
BLEED_MARGIN = 37.5
|
||||
SAFE_MARGIN = 75
|
||||
MAX_SAFE_X = TOTAL_WIDTH - SAFE_MARGIN
|
||||
MAX_SAFE_Y = TOTAL_HEIGHT - SAFE_MARGIN
|
||||
|
||||
# Icon info
|
||||
ICON_SIZE = 125
|
||||
ICON_MARGIN_LEFT = 0
|
||||
ICON_MARGIN_TOP = 0
|
||||
ICON_X = ICON_MARGIN_LEFT + SAFE_MARGIN
|
||||
ICON_Y = ICON_MARGIN_TOP + SAFE_MARGIN
|
||||
|
||||
# Banner info
|
||||
BANNER_WIDTH = 75
|
||||
BANNER_HEIGHT = 225
|
||||
BANNER_X = ICON_X + ICON_SIZE + 10
|
||||
BANNER_Y = 0
|
||||
BANNER_TEXT_FONT = "\"ethnocentric 12\""
|
||||
BANNER_TEXT_X = BANNER_X - 10
|
||||
BANNER_TEXT_Y = BANNER_HEIGHT // 3
|
||||
BANNER_TEXT_WIDTH = BANNER_WIDTH + 20
|
||||
|
||||
# Name info
|
||||
NAME_X = ICON_X + ICON_SIZE
|
||||
NAME_Y = SAFE_MARGIN + 10
|
||||
NAME_WIDTH = MAX_SAFE_X - NAME_X
|
||||
NAME_HEIGHT = 125
|
||||
NAME_FONT = "\"ethnocentric 11\""
|
||||
|
||||
NAME_WITH_BANNER_X = BANNER_X + BANNER_WIDTH
|
||||
NAME_WITH_BANNER_WIDTH = NAME_WIDTH - (NAME_WITH_BANNER_X - NAME_X)
|
||||
|
||||
def central_sym(x: int, y: int) -> Tuple[int, int]:
|
||||
return TOTAL_WIDTH - x, TOTAL_HEIGHT - y
|
||||
|
||||
# Alternative objects
|
||||
ALT_ICON_X, ALT_ICON_Y = central_sym(ICON_X, ICON_Y)
|
||||
ALT_BANNER_X, ALT_BANNER_Y = central_sym(BANNER_X, BANNER_Y)
|
||||
ALT_BANNER_TEXT_X, ALT_BANNER_TEXT_Y = central_sym(BANNER_TEXT_X, BANNER_TEXT_Y)
|
||||
|
||||
LAYOUT: str = f"""
|
||||
icon:
|
||||
x: {ICON_X}
|
||||
y: {ICON_Y}
|
||||
width: {ICON_SIZE}
|
||||
height: {ICON_SIZE}
|
||||
|
||||
alt_icon:
|
||||
extends: icon
|
||||
x: {ALT_ICON_X}
|
||||
y: {ALT_ICON_Y}
|
||||
angle: 3.14159
|
||||
|
||||
banner:
|
||||
x: {BANNER_X}
|
||||
y: {BANNER_Y}
|
||||
width: {BANNER_WIDTH}
|
||||
height: {BANNER_HEIGHT}
|
||||
|
||||
banner_text:
|
||||
x: {BANNER_TEXT_X}
|
||||
y: {BANNER_TEXT_Y}
|
||||
width: {BANNER_TEXT_WIDTH}
|
||||
height: 70
|
||||
font: {BANNER_TEXT_FONT}
|
||||
align: center
|
||||
# hint: blue
|
||||
|
||||
alt_banner:
|
||||
extends: banner
|
||||
x: {ALT_BANNER_X}
|
||||
y: {ALT_BANNER_Y}
|
||||
angle: 3.14159
|
||||
|
||||
|
||||
alt_banner_text:
|
||||
extends: banner_text
|
||||
angle: 3.14159
|
||||
x: {ALT_BANNER_TEXT_X}
|
||||
y: {ALT_BANNER_TEXT_Y}
|
||||
|
||||
name:
|
||||
x: {NAME_X}
|
||||
y: {NAME_Y}
|
||||
width: {NAME_WIDTH}
|
||||
height: {NAME_HEIGHT}
|
||||
align: center
|
||||
valign: middle
|
||||
font: {NAME_FONT}
|
||||
# hint: red
|
||||
|
||||
name_with_banner:
|
||||
extends: name
|
||||
x: {NAME_WITH_BANNER_X}
|
||||
width: {NAME_WITH_BANNER_WIDTH}
|
||||
|
||||
cut:
|
||||
x: {BLEED_MARGIN}
|
||||
y: {BLEED_MARGIN}
|
||||
width: {TOTAL_WIDTH - (2 * BLEED_MARGIN)}
|
||||
height: {TOTAL_HEIGHT - (2 * BLEED_MARGIN)}
|
||||
|
||||
safe:
|
||||
x: {SAFE_MARGIN}
|
||||
y: {SAFE_MARGIN}
|
||||
width: {TOTAL_WIDTH - (2 * SAFE_MARGIN)}
|
||||
height: {TOTAL_HEIGHT - (2 * SAFE_MARGIN)}
|
||||
radius: 16
|
||||
dash: 3 3
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
open("layout.yml", "w").write(LAYOUT)
|
||||
49
config.yml
Normal file
49
config.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
# Settings in the config.yml are overriding Squib's defaults. Anything in the main script will override this.
|
||||
|
||||
# Looking for DPI? It needs to be a parameter to Squib::Deck.new
|
||||
|
||||
#antialias: best #recommended. Only about 10% slower than fast
|
||||
#antialias: default # set the anti-aliasing algorithm. default defers to the underlying graphics device. See http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-antialias-t
|
||||
|
||||
# Text hints are used to show the boundaries of text boxes.
|
||||
# Can be enabled/disabled at the command-level, or set globally with `set`
|
||||
#text_hint: '#F00'
|
||||
|
||||
# Show progress bars on the command line for potentially long-running operations
|
||||
progress_bars: true
|
||||
|
||||
#Enable some custom colors that can be used in any color
|
||||
#custom_colors:
|
||||
# foo: '#abc'
|
||||
|
||||
#For reading image file command (e.g. png and svg), read from this directory instead
|
||||
img_dir: _graphics
|
||||
|
||||
# Use a SVG cairo back end, instead of an in-memory buffer
|
||||
# backend: :memory # default
|
||||
# backend: :svg # can create scalable pdfs, but rendering done at the printer level is not as good as Cairo.
|
||||
|
||||
# Configure what text markup uses replace characters
|
||||
# Below are the defaults
|
||||
# lsquote: "\u2018" #note that Yaml wants double quotes here to use escape chars
|
||||
# rsquote: "\u2019"
|
||||
# ldquote: "\u201C"
|
||||
# rdquote: "\u201D"
|
||||
# em_dash: "\u2014"
|
||||
# en_dash: "\u2013"
|
||||
# ellipsis: "\u2026"
|
||||
|
||||
# We can also disallow smart quotes and only allow explicit replacements with ``LaTeX-style'' quotes.
|
||||
# smart_quotes: false
|
||||
|
||||
# By default, Squib warns when a text box is ellipsized. This can get verbose
|
||||
# and can be turned off here
|
||||
# warn_ellipsize: true # default
|
||||
# warn_ellipsize: false # turn off entirely
|
||||
|
||||
# By default, Squib will warn if a PNG is being up-scaled.
|
||||
# warn_png_scale: true # default
|
||||
# warn_png_scale: false # turn off entirely
|
||||
|
||||
# How many pixels are in a "cell"?
|
||||
# cell_px: 37.5 # default
|
||||
37
deck.rb
Normal file
37
deck.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
require 'squib'
|
||||
|
||||
data = Squib.xlsx file: 'cards.xlsx', explode: 'quantite'
|
||||
|
||||
def icon_to_svg(icon)
|
||||
icon.nil? ? nil : "icone_#{icon}.svg"
|
||||
end
|
||||
|
||||
def rank_to_banner_svg(rank)
|
||||
rank.nil? ? nil : (rank.start_with?('+') || rank.start_with?('-')) ? "banniere_mod.svg" : "banniere_rang.svg"
|
||||
end
|
||||
|
||||
Squib::Deck.new(cards: data['nom'].size, layout: 'layout.yml') do
|
||||
background color: 'white'
|
||||
rect layout: 'cut'
|
||||
#rect layout: 'safe'
|
||||
|
||||
icon_file = data['icone'].map { |icon| icon_to_svg(icon) }
|
||||
svg file: icon_file, layout: 'icon'
|
||||
|
||||
banner_file = data['rang'].map { |rank| rank_to_banner_svg(rank) }
|
||||
svg file: banner_file, layout: 'banner'
|
||||
text str: data['rang'], layout: 'banner_text'
|
||||
|
||||
text str: data['nom'], layout: banner_file.map { |banner| banner.nil? ? 'name' : 'name_with_banner'}
|
||||
|
||||
alt_icon_file = data['icone_alt'].map { |icon| icon_to_svg(icon) }
|
||||
svg file: alt_icon_file, layout: 'alt_icon'
|
||||
|
||||
alt_banner_file = data['rang_alt'].map { |rank| rank_to_banner_svg(rank) }
|
||||
svg file: alt_banner_file, layout: 'alt_banner'
|
||||
text str: data['rang_alt'], layout: 'alt_banner_text'
|
||||
|
||||
save_sheet columns: 4
|
||||
save_pdf trim: 37.5, file: 'sheet.pdf'
|
||||
hand range: [0, 7, 15, 25, 38, 17], trim: 37.5
|
||||
end
|
||||
58
extract.py
Normal file
58
extract.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from typing import *
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
|
||||
GRAPHICS_FILE: str = "graphics.svg"
|
||||
OUTPUT_DIR: str = "_graphics"
|
||||
ID_REGEX: re.Pattern = re.compile("rbk_.*")
|
||||
|
||||
|
||||
def get_all_ids(svg_file_name: str) -> List[str]:
|
||||
objects: bytes = subprocess.check_output([
|
||||
"inkscape",
|
||||
"--query-all",
|
||||
svg_file_name,
|
||||
])
|
||||
return [
|
||||
obj.split(b",")[0].decode("utf-8")
|
||||
for obj in
|
||||
objects.splitlines()
|
||||
]
|
||||
|
||||
|
||||
def id_filter(svg_id: str) -> bool:
|
||||
return ID_REGEX.match(svg_id) is not None
|
||||
|
||||
|
||||
def export_objects(svg_file: str, object_ids: Iterable[str]) -> None:
|
||||
|
||||
for obj in object_ids:
|
||||
command = [
|
||||
"inkscape",
|
||||
"--export-type=svg",
|
||||
"--export-plain-svg",
|
||||
"--vacuum-defs",
|
||||
"--export-id-only",
|
||||
"--export-id",
|
||||
obj,
|
||||
"-o",
|
||||
OUTPUT_DIR + "/" + obj[4:],
|
||||
svg_file,
|
||||
]
|
||||
print(f"exporting {obj[4:]}...", end="")
|
||||
res = subprocess.check_output(command)
|
||||
if res:
|
||||
print(f" An error might have occurred:\n{res}")
|
||||
else:
|
||||
print(" done")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not os.path.isdir(OUTPUT_DIR):
|
||||
os.mkdir(OUTPUT_DIR)
|
||||
export_objects(
|
||||
GRAPHICS_FILE,
|
||||
filter(id_filter, get_all_ids(GRAPHICS_FILE)),
|
||||
)
|
||||
10221
graphics.svg
Normal file
10221
graphics.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 544 KiB |
Reference in New Issue
Block a user