Commit 0432a180 authored by Vivien Kraus's avatar Vivien Kraus
Browse files

Merge the renderer

parent 9a3ee719
......@@ -608,15 +608,6 @@ which has the usual Gtk+ options, and a batch command-line interface
program, =tarot-cli=.
** Invoking tarot-cli
#+texinfo: @include invoke-tarot-cli-body.texi
* Rendering a game
The program =tarot-render= is able to load a game and render it as a
PNG file. It serves as a demonstration for the cairo painting of the
cards.
#+attr_texinfo: :width 1in :alt The representation of a game in the GUI
[[file:tarot-figures/example.png]]
** Invoking tarot-render
#+texinfo: @include invoke-tarot-render-body.texi
* Generating a game with Monte Carlo Tree Search
The program =tarot-generate= generates a game in which all players
play with Monte Carlo Tree Search.
......
......@@ -24,14 +24,12 @@ dist_tarotfigures_DATA = \
%canon_reldir%_tarot_TEXINFOS = \
%reldir%/fdl.texi \
%reldir%/invoke-tarot-render-body.texi \
%reldir%/invoke-tarot-generate-body.texi \
%reldir%/invoke-tarot-cli-body.texi
EXTRA_DIST += \
%reldir%/tarot-figures/example.eps \
%reldir%/tarot-figures/example-tree.eps \
%reldir%/invoke-tarot-render-body.texi \
%reldir%/invoke-tarot-generate-body.texi \
%reldir%/invoke-tarot-cli-body.texi
......@@ -40,7 +38,6 @@ MAINTAINERCLEANFILES += \
$(srcdir)/%reldir%/tarot-figures/example.eps \
$(srcdir)/%reldir%/tarot-figures/example-tree.png \
$(srcdir)/%reldir%/tarot-figures/example-tree.eps \
$(srcdir)/%reldir%/invoke-tarot-render-body.texi \
$(srcdir)/%reldir%/invoke-tarot-generate-body.texi \
$(srcdir)/%reldir%/invoke-tarot-cli-body.texi
......
......@@ -32,8 +32,8 @@ tree_render_verbose_0 = @echo " TREE " $@;
if ENABLE_RENDERER
if WITH_CAIRO
$(srcdir)/%reldir%/example.xml.png: %reldir%/example.xml src/cairo/tarot-render
$(render_verbose) src/cairo/tarot-render $(srcdir)/%reldir%/example.xml
$(srcdir)/%reldir%/example.xml.png: %reldir%/example.xml src/tarot-cli
$(render_verbose) src/tarot-cli --render -i $(srcdir)/%reldir%/example.xml -o $(srcdir)/%reldir%/example.xml.png
else # not WITH_CAIRO
$(srcdir)/%reldir%/example.xml.png: %reldir%/example.xml
@echo "A game needs to be re-rendered. You need to enable the cairo bindings."
......
......@@ -18,7 +18,6 @@
include %reldir%/cardpainter-vapi.am
include %reldir%/libtarot-cardpainter.am
include %reldir%/tarot-render-program.am
AM_VALAFLAGS += --pkg cairo
......
/**
* file main.c
*
* Copyright (C) 2018 Vivien Kraus <vivien@planete-kraus.eu>
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <tarot.h>
#include <tarot-gobject.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tarot-render-opts.h"
int tarot_render (const char *input_filename, int default_view,
TarotPlayer view, size_t width, size_t height);
int
tarot_run (const char *filename)
{
return tarot_render (filename,
!(HAVE_OPT (AS_PLAYER) && ENABLED_OPT (AS_PLAYER)),
OPT_ARG (AS_PLAYER),
OPT_ARG (WIDTH), OPT_ARG (HEIGHT));
}
Autogen Definitions options;
prog-name = tarot-render;
prog-title = "The Amazing Rules Of TAROT -- Rendering";
long-opts;
gnu-usage; /* GNU style preferred to default */
config-header = "config.h";
argument = "[FILES...]";
main = {
main-type = for-each;
handler-proc = "tarot_run";
};
flag = {
name = as-player;
value = p;
descrip = "View the game as this player";
disable = not;
doc = "Set the player from which we view the cards. If not present or disabled, then use the player that were dealt cards in the game. If the game has a deal for all players, then you have to set this option.";
};
flag = {
name = width;
value = w;
arg-default = 640;
arg-type = number;
descrip = "Width of the output file";
doc = "Set the width for the output image.";
};
flag = {
name = height;
value = h;
arg-default = 480;
arg-type = number;
descrip = "Height of the output file";
doc = "Set the height for the output image.";
};
\ No newline at end of file
# tarot-render-program.am
#
# Copyright (C) 2017, 2018 Vivien Kraus <vivien@planete-kraus.eu>
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
if WITH_CAIRO
bin_PROGRAMS += %reldir%/tarot-render
dist_man6_MANS += %reldir%/tarot-render.man
%canon_reldir%_tarot_render_SOURCES = \
%reldir%/tarot_render.c \
%reldir%/card_painter.h \
%reldir%/tarot-render-opts.c \
%reldir%/tarot-render-opts.h \
%reldir%/tarot-render-main.c
%canon_reldir%_tarot_render_CFLAGS = \
$(AM_CFLAGS) \
$(GOBJECT_CFLAGS) \
$(CAIRO_CFLAGS) \
$(LIBOPTS_CFLAGS) \
-w
%canon_reldir%_tarot_render_CPPFLAGS = \
$(AM_CPPFLAGS) \
-include config.h
%canon_reldir%_tarot_render_LDADD = \
src/libtarot.la \
src/libtarot-gobject.la \
%reldir%/libtarot-painter.la \
lib-program/libgnuprogram.la \
$(LIBOPTS_LDADD) \
$(GOBJECT_LIBS) \
$(CAIRO_LIBS)
endif # WITH_CAIRO
$(%canon_reldir%_tarot_render_OBJECTS): %reldir%/tarot-render-opts.h src/tarot-gobject.h
$(srcdir)/%reldir%/tarot-render-opts.h: %reldir%/tarot-render-opts.c
$(srcdir)/%reldir%/tarot-render-opts.c: %reldir%/tarot-render-opts.def
$(AM_V_GEN) cd $(srcdir)/%reldir% && $(AUTOGEN) --writable tarot-render-opts.def
$(srcdir)/%reldir%/tarot-render.man: %reldir%/tarot-render-opts.def
$(AM_V_GEN) cd $(srcdir)/%reldir% && $(AUTOGEN) --writable -Tagman-cmd.tpl -btarot tarot-render-opts.def \
&& mv tarot-render.1 tarot-render.man
$(srcdir)/doc/invoke-tarot-render-body.texi: %reldir%/tarot-render-opts.def
@rm -rf %reldir%/autogen-texi-tarot-render
@mkdir %reldir%/autogen-texi-tarot-render
@cp $(srcdir)/%reldir%/tarot-render-opts.def %reldir%/autogen-texi-tarot-render/
$(AM_V_GEN) cd %reldir%/autogen-texi-tarot-render && $(AUTOGEN) --writable -Tagtexi-cmd.tpl tarot-render-opts.def
@tail -n +3 %reldir%/autogen-texi-tarot-render/invoke-tarot-render.texi > $(srcdir)/doc/invoke-tarot-render-body.texi
@rm -rf %reldir%/autogen-texi-tarot-render
$(srcdir)/%reldir%/tarot_render.c: %reldir%/tarot_render.vala \
src/tarot-@LIBTOOL_CURRENT@.0.vapi \
%reldir%/cardpainter-@LIBTOOL_CURRENT@.0.vapi
$(AM_V_VALAC) cd $(srcdir) && $(VALAC) $(AM_VALAFLAGS) $(VALAFLAGS) \
-C \
%reldir%/tarot_render.vala \
src/tarot-@LIBTOOL_CURRENT@.0.vapi \
%reldir%/cardpainter-@LIBTOOL_CURRENT@.0.vapi \
&& touch %reldir%/tarot_render.c
EXTRA_DIST += \
%reldir%/tarot_render.vala \
%reldir%/tarot_render.c \
%reldir%/tarot-render-opts.def \
%reldir%/tarot-render-opts.c \
%reldir%/tarot-render-opts.h \
%reldir%/tarot-render-main.c \
%reldir%/tarot-render.man
MAINTAINERCLEANFILES += \
$(srcdir)/%reldir%/tarot_render.c \
$(srcdir)/%reldir%/tarot-render.man \
$(srcdir)/%reldir%/tarot-render-opts.c \
$(srcdir)/%reldir%/tarot-render-opts.h
INDENTED += %reldir%/tarot-render-main.c
// file tarot_render.vala
//
// Copyright (C) 2017, 2018 Vivien Kraus <vivien@planete-kraus.eu>
//
// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see
// <http://www.gnu.org/licenses/>.
namespace Tarot {
private static int read_whole_file (string filename, out string contents) {
var file = GLib.FileStream.open (filename, "r");
contents = "";
if (file == null) {
stderr.printf ("ERROR: could not load file %s\n", filename);
return 1;
} else {
char[] ret = new char[0];
while (!file.eof ()) {
char c = (char) file.getc ();
if (!file.eof ()) {
ret += c;
}
}
ret += '\0';
contents = (string) ret;
return 0;
}
}
public static int render (string input_filename,
bool default_view,
Tarot.Player view,
size_t width,
size_t height) {
var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, (int) width, (int) height);
var context = new Cairo.Context (surface);
context.translate (width / 2.0, height / 2.0);
context.scale (1, -1);
var game = new Tarot.Game.alloc ();
string contents;
if (read_whole_file (input_filename, out contents) != 0) {
return 1;
}
if (game.load_from_xml (contents) != 0) {
stderr.printf ("ERROR: This game is not valid.\n");
return 1;
}
var painter = new Tarot.DefaultCardPainter ();
if (default_view && !painter.paint_game (width, height, game, context)) {
stderr.printf ("ERROR: This game does not have a default player.\n");
return 1;
} else if (!default_view) {
painter.paint_game_as (width, height, game, view, context);
}
var status = surface.write_to_png (input_filename + ".png");
if (status == Cairo.Status.SUCCESS) {
return 0;
}
stderr.printf ("Error: could not write PNG file: %s\n", status.to_string ());
return 1;
}
}
\ No newline at end of file
......@@ -27,10 +27,11 @@ include = "int tarot_run_dog (char *input, TarotCardArray *cards, char **output)
include = "int tarot_run_discard (char *input, TarotCardArray *cards, char **output);";
include = "int tarot_run_handful (char *input, TarotCardArray *cards, char **output);";
include = "int tarot_run_play (char *input, TarotCard card, char **output);";
include = "int tarot_run_render (char *input, int w, int h, char **output, int *output_length);";
include = "int opts_main (int argc, char **argv);";
include = "int main (int argc, char **argv) { setenv (\"SYSCONFDIR\", SYSCONFDIR, 0); return opts_main (argc, argv); }";
include = "static char *read_input ();";
include = "static void write_output (char *output);";
include = "static void write_output (char *output, size_t len);";
include = "#define main opts_main";
include = <<- END
......@@ -66,14 +67,21 @@ read_input ()
}
static void
write_output (char *output)
write_output (char *output, size_t len)
{
if (output != NULL)
if (output != NULL && len != 0)
{
if (HAVE_OPT (OUTPUT))
{
fputs (output, OPT_VALUE_OUTPUT);
fclose (OPT_VALUE_OUTPUT);
FILE *file = fopen (OPT_ARG (OUTPUT), "w");
if (file == NULL)
{
fprintf (stderr, "ERROR: could not open the output file '%s'.\n",
OPT_ARG (OUTPUT));
exit (1);
}
fwrite (output, 1, len, file);
fclose (file);
}
else
{
......@@ -225,40 +233,50 @@ main = {
TarotCard card;
char *end;
int decl;
size_t output_len = 0;
int i_output_len;
switch (WHICH_IDX_NEW_GAME)
{
case INDEX_OPT_STATUS:
input = read_input ();
res = tarot_run_status (input, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_IMPUTE:
input = read_input ();
res = tarot_run_impute (input, OPT_ARG (SEED), OPT_VALUE_RETRY, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_MCTS:
input = read_input ();
res = tarot_run_mcts (input, OPT_ARG (SEED), OPT_VALUE_N_ITERATIONS, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_MCTS_TREE:
input = read_input ();
res = tarot_run_mcts_tree (input, OPT_ARG (SEED), OPT_VALUE_N_ITERATIONS, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_NEW_GAME:
res = tarot_run_new_game (OPT_VALUE_N_PLAYERS, ENABLED_OPT (CALL_ALLOWED) && HAVE_OPT (CALL_ALLOWED), &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_DEAL_TO:
input = read_input ();
parse_as_named_card_array (OPT_ARG (DEAL_TO), &whom, &cards);
res = tarot_run_deal_to (input, whom, cards, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_DEAL_ALL:
input = read_input ();
players = parse_as_player_array (OPT_ARG (DEAL_ALL));
res = tarot_run_deal_all (input, players, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_DEAL_RANDOM:
input = read_input ();
res = tarot_run_deal_random (input, OPT_ARG (SEED), OPT_VALUE_RETRY, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_BID:
input = read_input ();
......@@ -284,6 +302,7 @@ main = {
break;
};
res = tarot_run_bid (input, bid, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_DECL:
input = read_input ();
......@@ -300,6 +319,7 @@ main = {
break;
};
res = tarot_run_decl (input, decl, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_CALL:
input = read_input ();
......@@ -310,11 +330,13 @@ main = {
USAGE (1);
}
res = tarot_run_call (input, card, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_DOG:
input = read_input ();
cards = parse_as_card_array (OPT_ARG (DOG));
res = tarot_run_dog (input, cards, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_DISCARD:
input = read_input ();
......@@ -327,6 +349,7 @@ main = {
cards = parse_as_card_array (OPT_ARG (DISCARD));
}
res = tarot_run_discard (input, cards, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_HANDFUL:
input = read_input ();
......@@ -339,6 +362,7 @@ main = {
cards = parse_as_card_array (OPT_ARG (HANDFUL));
}
res = tarot_run_handful (input, cards, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_PLAY:
input = read_input ();
......@@ -349,9 +373,15 @@ main = {
USAGE (1);
}
res = tarot_run_play (input, card, &output);
output_len = (output == NULL ? 0 : strlen (output));
break;
case INDEX_OPT_RENDER:
input = read_input ();
res = tarot_run_render (input, OPT_ARG (WIDTH), OPT_ARG (HEIGHT), &output, &i_output_len);
output_len = i_output_len;
break;
}
write_output (output);
write_output (output, output_len);
free (input);
tarot_card_array_free (cards);
tarot_player_array_free (players);
......@@ -491,6 +521,15 @@ flag = {
doc = 'In this mode, the next player plays a card.';
};
flag = {
name = render;
no-preset;
equivalence = new-game;
descrip = "Render a game to a PNG file";
doc = 'This mode will read a game from standard input and represent it as PNG. You @strong{should} pass an @command{--output} parameter, otherwise your shell will try to print the binary contents. You can also specify the @command{--width} and @command{--height} of the resulting file. For instance, the example game renders as:
@image{tarot-figures/example,10cm}';
};
flag = {
name = status;
no-preset;
......@@ -551,9 +590,7 @@ flag = {
value = o;
descrip = "Output file to write";
arg-type = file;
open-file = "yes";
file-mode = "w";
doc = 'When @command{tarot-cli} generates a game, it will be saved to disk instead of printed to the standard output.';
doc = 'When @command{tarot-cli} generates a game, it will be saved to disk instead of printed to the standard output. @command{--render} also needs an output file, which should be a png file.';
};
flag = {
......@@ -566,4 +603,22 @@ flag = {
open-file = "yes";
file-mode = "r";
doc = 'When @command{tarot-cli} expects to read a game, it will be read from disk instead of the standard input.';
};
flag = {
name = width;
value = w;
arg-default = 640;
arg-type = number;
descrip = "Width of the output file";
doc = "Set the width for the output image.";
};
flag = {
name = height;
value = h;
arg-default = 480;
arg-type = number;
descrip = "Height of the output file";
doc = "Set the height for the output image.";
};
\ No newline at end of file
......@@ -18,6 +18,7 @@
TAROT_CLI_SOURCES_VALA = $(srcdir)/%reldir%/tarot_cli.vala \
$(srcdir)/%reldir%/tarot-@LIBTOOL_CURRENT@.0.vapi \
$(srcdir)/%reldir%/cairo/cardpainter-@LIBTOOL_CURRENT@.0.vapi \
$(srcdir)/%reldir%/engine/status.vala \
$(srcdir)/%reldir%/engine/pretty_status.vala
......@@ -26,6 +27,7 @@ TAROT_CLI_SOURCES_C = $(srcdir)/%reldir%/tarot_cli.c \
$(srcdir)/%reldir%/engine/pretty_status.c
if WITH_GOBJECT
if WITH_CAIRO
bin_PROGRAMS += %reldir%/tarot-cli
dist_man6_MANS += %reldir%/tarot-cli.man
%canon_reldir%_tarot_cli_SOURCES = \
......@@ -33,11 +35,13 @@ dist_man6_MANS += %reldir%/tarot-cli.man
%reldir%/engine/status.c \
%reldir%/engine/pretty_status.c \
%reldir%/tarot-cli-opts.c \
%reldir%/tarot-cli-opts.h
%reldir%/tarot-cli-opts.h \
%reldir%/cairo/card_painter.h
%canon_reldir%_tarot_cli_CFLAGS = \
$(AM_CFLAGS) \
$(LIBOPTS_CFLAGS) \
$(GOBJECT_CFLAGS) \
$(CAIRO_CFLAGS) \
-w
%canon_reldir%_tarot_cli_CPPFLAGS = \
$(AM_CPPFLAGS) \
......@@ -45,9 +49,13 @@ dist_man6_MANS += %reldir%/tarot-cli.man
%canon_reldir%_tarot_cli_LDADD = \
%reldir%/libtarot.la \
%reldir%/libtarot-gobject.la \
%reldir%/cairo/libtarot-painter.la \
lib-program/libgnuprogram.la \
$(LIBOPTS_LDADD) \
$(GOBJECT_LIBS)
$(GOBJECT_LIBS) \
$(CAIRO_LIBS)
TESTS += $(TAROT_CLI_TESTS)
endif # WITH_CAIRO
endif # WITH_OBJECT
dist_sysconf_DATA += %reldir%/tarot.conf
......@@ -79,8 +87,6 @@ EXTRA_DIST += %reldir%/tarot-cli.man \
%reldir%/engine/status.vala \
%reldir%/engine/pretty_status.vala
TESTS += $(TAROT_CLI_TESTS)
MAINTAINERCLEANFILES += \
$(srcdir)/%reldir%/tarot-cli.man \
$(srcdir)/%reldir%/tarot-cli-opts.c \
......
......@@ -341,4 +341,34 @@ namespace Tarot {
output = game.save_to_xml ();
return 0;
}
public static int run_render (string input, int width, int height, out char[] output) {
var game = new Tarot.Game.alloc ();
output = new char[0];
if (game.load_from_xml (input) != 0) {
stderr.printf ("ERROR: this game is not valid.\n");
return 1;
}
var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height);
var context = new Cairo.Context (surface);
context.translate (width / 2.0, height / 2.0);
context.scale (1, -1);
var painter = new Tarot.DefaultCardPainter ();
if (!painter.paint_game (width, height, game, context)) {
stderr.printf ("ERROR: This game does not have a default player.\n");
return 1;
}
var total = new char[0];
var status = surface.write_to_png_stream ((data) => {
foreach (uchar u in data) {
total += (char) u;
}
return Cairo.Status.SUCCESS;
});
if (status == Cairo.Status.SUCCESS) {
output = total;
return 0;
}
stderr.printf ("Error: could not write PNG: %s\n", status.to_string ());
return 1;
}
}
\ No newline at end of file
......@@ -60,4 +60,11 @@
...
obj:/usr/bin/bash
...
}
{
fontconfig
Memcheck:Leak
fun:*alloc
...
obj:*/libfontconfig*
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment