1
0

initial commit

This commit is contained in:
Ninjananas
2024-08-31 12:29:28 +02:00
commit 7bdea04111
12 changed files with 10584 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
_output/*.png
_output/*.pdf
~$*
.DS_Store
._graphics_compute_touch
layout.yml
_graphics

3
Gemfile Normal file
View File

@@ -0,0 +1,3 @@
source 'https://rubygems.org'
gem 'squib'

9
Makefile Normal file
View 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
View 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
View File

@@ -0,0 +1,7 @@
require 'squib'
task default: [:deck]
task :deck do
load 'deck.rb'
end

1
_output/gitkeep.txt Normal file
View 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

Binary file not shown.

119
compute_layout.py Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 544 KiB