mirror of
https://github.com/Oreolek/gamebookformat.git
synced 2024-06-16 15:10:45 +03:00
Use term section instead of paragraph.
Because it will become confusing later when there is support for sections split up in several paragraphs otherwise.
This commit is contained in:
parent
81f27d0648
commit
5d42472385
|
@ -33,7 +33,7 @@ import os.path
|
|||
import sys
|
||||
import json
|
||||
|
||||
import paragraphs
|
||||
import sections
|
||||
|
||||
from output import OutputFormat
|
||||
from latex import LatexFormat
|
||||
|
@ -57,7 +57,7 @@ def make_supported_formats_list_string():
|
|||
|
||||
def format_gamebook(inputfilenames, outputfilename):
|
||||
output_format = find_output_format(outputfilename)
|
||||
book = paragraphs.Book()
|
||||
book = sections.Book()
|
||||
for inputfilename in inputfilenames:
|
||||
parse_file_to_book(open(inputfilename, 'r'), book)
|
||||
output_format.write(book, open(outputfilename, 'w'))
|
||||
|
@ -69,7 +69,7 @@ def parse_file_to_book(inputfile, book):
|
|||
for line in inputfile.readlines():
|
||||
if line.startswith('*'):
|
||||
if name:
|
||||
book.add(paragraphs.Paragraph(name, text), number)
|
||||
book.add(sections.Section(name, text), number)
|
||||
number = None
|
||||
text = ""
|
||||
heading = line[1:].strip().split(' ')
|
||||
|
@ -79,11 +79,11 @@ def parse_file_to_book(inputfile, book):
|
|||
number = int(heading[0])
|
||||
name = heading[1]
|
||||
else:
|
||||
raise Exception("bad paragraph heading %s" % str(heading))
|
||||
raise Exception("bad section heading %s" % str(heading))
|
||||
else:
|
||||
text = text + " " + line.strip()
|
||||
if name:
|
||||
book.add(paragraphs.Paragraph(name, text), number)
|
||||
book.add(sections.Section(name, text), number)
|
||||
|
||||
def find_output_format(outputfilename):
|
||||
for of in OUTPUT_FORMATS:
|
||||
|
|
34
output.py
34
output.py
|
@ -13,7 +13,7 @@ class OutputFormat (object):
|
|||
|
||||
def write(self, book, output):
|
||||
self.write_begin(book, output)
|
||||
self.write_shuffled_paragraphs(book.shuffle(), output)
|
||||
self.write_shuffled_sections(book.shuffle(), output)
|
||||
self.write_end(book, output)
|
||||
|
||||
def write_begin(self, book, output):
|
||||
|
@ -21,17 +21,17 @@ class OutputFormat (object):
|
|||
'max' : book.max
|
||||
},
|
||||
|
||||
def write_shuffled_paragraphs(self, shuffled_paragraphs, output):
|
||||
for p in shuffled_paragraphs.as_list[1:]:
|
||||
self.write_paragraph(p, shuffled_paragraphs, output)
|
||||
def write_shuffled_sections(self, shuffled_sections, output):
|
||||
for p in shuffled_sections.as_list[1:]:
|
||||
self.write_section(p, shuffled_sections, output)
|
||||
|
||||
def write_paragraph(self, paragraph, shuffled_paragraphs, output):
|
||||
def write_section(self, section, shuffled_sections, output):
|
||||
refs = []
|
||||
refsdict = ReferenceFormatter(paragraph, shuffled_paragraphs,
|
||||
self.load_template("paragraph_ref"))
|
||||
formatted_text = paragraph.format(refsdict)
|
||||
print >> output, self.load_template("paragraph") % {
|
||||
'nr' : shuffled_paragraphs.to_nr[paragraph],
|
||||
refsdict = ReferenceFormatter(section, shuffled_sections,
|
||||
self.load_template("section_ref"))
|
||||
formatted_text = section.format(refsdict)
|
||||
print >> output, self.load_template("section") % {
|
||||
'nr' : shuffled_sections.to_nr[section],
|
||||
'text' : formatted_text,
|
||||
'refs' : '\n'.join(refsdict.getfound()) # hack for DOT output
|
||||
},
|
||||
|
@ -58,19 +58,19 @@ class OutputFormat (object):
|
|||
|
||||
class ReferenceFormatter (object):
|
||||
"There is probably a better way, but this hack seems to work."
|
||||
def __init__(self, paragraph, shuffled_paragraphs, ref_template):
|
||||
self.paragraph = paragraph
|
||||
self.shuffled_paragraphs = shuffled_paragraphs
|
||||
def __init__(self, section, shuffled_sections, ref_template):
|
||||
self.section = section
|
||||
self.shuffled_sections = shuffled_sections
|
||||
self.found = set()
|
||||
self.ref_template = ref_template
|
||||
|
||||
def __getitem__(self, key):
|
||||
to_paragraph = self.shuffled_paragraphs.from_name[key]
|
||||
to_section = self.shuffled_sections.from_name[key]
|
||||
res = self.ref_template % {
|
||||
'nr' : self.shuffled_paragraphs.to_nr[to_paragraph],
|
||||
'from_nr' : self.shuffled_paragraphs.to_nr[self.paragraph]
|
||||
'nr' : self.shuffled_sections.to_nr[to_section],
|
||||
'from_nr' : self.shuffled_sections.to_nr[self.section]
|
||||
}
|
||||
if key in self.shuffled_paragraphs.name_to_nr:
|
||||
if key in self.shuffled_sections.name_to_nr:
|
||||
self.found.add(res)
|
||||
return res
|
||||
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
import random
|
||||
import sys
|
||||
|
||||
class Paragraph:
|
||||
def __init__(self, name, text):
|
||||
self.name = name
|
||||
self.text = text
|
||||
|
||||
def __repr__(self):
|
||||
return "Paragraph(%s, %s)" % (repr(self.name), repr(self.text))
|
||||
|
||||
def format(self, references):
|
||||
return self.text % references
|
||||
|
||||
class ShuffledParagraphs:
|
||||
def __init__(self, as_list, from_nr, to_nr, from_name):
|
||||
self.as_list = as_list
|
||||
self.from_nr = from_nr
|
||||
self.to_nr = to_nr
|
||||
self.from_name = from_name
|
||||
self.name_to_nr = {}
|
||||
for n in from_name:
|
||||
self.name_to_nr[n] = to_nr[from_name[n]]
|
||||
|
||||
class Book:
|
||||
def __init__(self):
|
||||
self.paragraphs = []
|
||||
self.nr_paragraphs = {}
|
||||
self.max = 0
|
||||
|
||||
def add(self, paragraph, nr=None):
|
||||
self.paragraphs.append(paragraph)
|
||||
if len(self.paragraphs) > self.max:
|
||||
self.max = len(self.paragraphs)
|
||||
if nr:
|
||||
self.nr_paragraphs[nr] = paragraph
|
||||
if nr > self.max:
|
||||
self.max = nr
|
||||
|
||||
def shuffle(self):
|
||||
as_list = [None]
|
||||
from_nr = {}
|
||||
to_nr = {}
|
||||
from_name = {}
|
||||
shuffled = self.paragraphs[:]
|
||||
for p in self.nr_paragraphs.values():
|
||||
shuffled.remove(p)
|
||||
random.shuffle(shuffled)
|
||||
for nr in range(1, self.max + 1):
|
||||
if self.nr_paragraphs.has_key(nr):
|
||||
paragraph = self.nr_paragraphs[nr]
|
||||
elif len(shuffled):
|
||||
paragraph = shuffled.pop()
|
||||
else:
|
||||
paragraph = None
|
||||
as_list.append(paragraph)
|
||||
from_nr[nr] = paragraph
|
||||
if paragraph:
|
||||
to_nr[paragraph] = nr
|
||||
from_name[paragraph.name] = paragraph
|
||||
return ShuffledParagraphs(as_list, from_nr, to_nr, from_name)
|
|
@ -9,7 +9,7 @@ gamebook on paper or a screen (or for debugging it).
|
|||
|------------------+-----------+-------------------------------------------------------------------------------------------------|
|
||||
| LaTeX | .tex | Useful to generate PDFs using pdflatex or whatever LaTeX tools you prefer. |
|
||||
| Rich Text Format | .rtf | Supported because the Windhammer Prize requires it. |
|
||||
| Graphviz DOT | .dot | Use with the Graphviz dot tool to generate a flowchart graph of all paragraphs in the gamebook. |
|
||||
| Graphviz DOT | .dot | Use with the Graphviz dot tool to generate a flowchart graph of all sections in the gamebook. |
|
||||
| HTML | .html | Play gamebook in browser. |
|
||||
| Debug Plain Text | .debug | Plain text debug output of gamebook contents. |
|
||||
|
||||
|
@ -18,7 +18,7 @@ More to be added.
|
|||
** Gamebook Format
|
||||
|
||||
The input file expected by the formatgamebook.py script must be in a
|
||||
format containing information about all paragraphs in the book
|
||||
format containing information about all sections in the book
|
||||
plus some optional metadata. The format should (when TBD) be documented
|
||||
here, but it is not expected that most users would ever have to look
|
||||
at the gamebook file, but rather use a higher level format or a
|
||||
|
|
61
sections.py
Normal file
61
sections.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
import random
|
||||
import sys
|
||||
|
||||
class Section:
|
||||
def __init__(self, name, text):
|
||||
self.name = name
|
||||
self.text = text
|
||||
|
||||
def __repr__(self):
|
||||
return "Section(%s, %s)" % (repr(self.name), repr(self.text))
|
||||
|
||||
def format(self, references):
|
||||
return self.text % references
|
||||
|
||||
class ShuffledSections:
|
||||
def __init__(self, as_list, from_nr, to_nr, from_name):
|
||||
self.as_list = as_list
|
||||
self.from_nr = from_nr
|
||||
self.to_nr = to_nr
|
||||
self.from_name = from_name
|
||||
self.name_to_nr = {}
|
||||
for n in from_name:
|
||||
self.name_to_nr[n] = to_nr[from_name[n]]
|
||||
|
||||
class Book:
|
||||
def __init__(self):
|
||||
self.sections = []
|
||||
self.nr_sections = {}
|
||||
self.max = 0
|
||||
|
||||
def add(self, section, nr=None):
|
||||
self.sections.append(section)
|
||||
if len(self.sections) > self.max:
|
||||
self.max = len(self.sections)
|
||||
if nr:
|
||||
self.nr_sections[nr] = section
|
||||
if nr > self.max:
|
||||
self.max = nr
|
||||
|
||||
def shuffle(self):
|
||||
as_list = [None]
|
||||
from_nr = {}
|
||||
to_nr = {}
|
||||
from_name = {}
|
||||
shuffled = self.sections[:]
|
||||
for p in self.nr_sections.values():
|
||||
shuffled.remove(p)
|
||||
random.shuffle(shuffled)
|
||||
for nr in range(1, self.max + 1):
|
||||
if self.nr_sections.has_key(nr):
|
||||
section = self.nr_sections[nr]
|
||||
elif len(shuffled):
|
||||
section = shuffled.pop()
|
||||
else:
|
||||
section = None
|
||||
as_list.append(section)
|
||||
from_nr[nr] = section
|
||||
if section:
|
||||
to_nr[section] = nr
|
||||
from_name[section.name] = section
|
||||
return ShuffledSections(as_list, from_nr, to_nr, from_name)
|
|
@ -1,2 +1,2 @@
|
|||
BEGIN DEBUG OUTPUT
|
||||
Number of paragraphs: %(max)d
|
||||
Number of sections: %(max)d
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
*1 start
|
||||
This is where the adventure begins. You can go on to the
|
||||
next paragraph, see %(next)s or try the other instead, see %(other)s.
|
||||
next section, see %(next)s or try the other instead, see %(other)s.
|
||||
|
||||
*next
|
||||
This is the next paragraph. Go on to the end at %(end)s.
|
||||
This is the next section. Go on to the end at %(end)s.
|
||||
|
||||
*other
|
||||
This is another paragraph. You can try the next paragraph now,
|
||||
This is another section. You can try the next section now,
|
||||
see %(next)s, or go on to the end, see %(end)s.
|
||||
|
||||
*end
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
#!/usr/bin/env python2.5
|
||||
|
||||
import unittest
|
||||
from unittest import TestCase
|
||||
|
||||
import paragraphs
|
||||
|
||||
class TestParagraph(TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_create(self):
|
||||
p = paragraphs.Paragraph("foo")
|
||||
self.assertEqual(p.name, "foo")
|
||||
|
||||
class TestBook(TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_create(self):
|
||||
g = paragraphs.Book()
|
||||
self.assertEqual(g.paragraphs, [])
|
||||
self.assertEqual(g.nr_paragraphs, {})
|
||||
self.assertEqual(g.max, 0)
|
||||
|
||||
def test_old_big_mess(self):
|
||||
"""This was tested using print statements in the old
|
||||
paragraphs __main__. Not pretty. Keeping it here anyway
|
||||
as long as it doesn't cause too many problems to update."""
|
||||
from paragraphs import paragraph_refs_format
|
||||
g = paragraphs.Book()
|
||||
c = paragraphs.Paragraph("c", "aaac")
|
||||
g.add(paragraphs.Paragraph("a", "aaa"))
|
||||
g.add(paragraphs.Paragraph("b", "aaab"))
|
||||
g.add(c)
|
||||
g.add(paragraphs.Paragraph("d", "aaad"), 22)
|
||||
g.add(paragraphs.Paragraph("e", "aaae"), 1)
|
||||
g.add(paragraphs.Paragraph("f", "aaaf",
|
||||
[paragraphs.ParagraphItem("fff")]))
|
||||
g.add(paragraphs.Paragraph("g", "aaag",
|
||||
[paragraphs.ParagraphItem("ggg", "G")]))
|
||||
m = paragraphs.Paragraph("m")
|
||||
m.addtext("m")
|
||||
m.addtext("t")
|
||||
g.add(m)
|
||||
shuffled = g.shuffle()
|
||||
|
||||
self.assertEqual(len(shuffled.as_list), 23)
|
||||
self.assertEqual(paragraph_refs_format('abc', []), 'abc')
|
||||
self.assertEqual(paragraph_refs_format('abc%%z', []), 'abc%z')
|
||||
self.assertEqual(paragraph_refs_format(
|
||||
'abc%sx',
|
||||
[paragraphs.Paragraph("p1", "111")]),
|
||||
'abcp1x')
|
||||
self.assertEqual(paragraph_refs_format(
|
||||
'abc%ry', [paragraphs.Paragraph("p2", "222")]),
|
||||
"abc'p2'y")
|
||||
self.assertEqual(paragraph_refs_format('%%a%nbc%su', ["f", c],
|
||||
shuffled),
|
||||
'%%a%dbccu' % shuffled.name_to_nr["f"])
|
||||
self.assertEqual(paragraph_refs_format('abc%nu', ["c"], shuffled),
|
||||
'abc%du' % shuffled.name_to_nr["c"])
|
||||
self.assertEqual(paragraph_refs_format('%s', [m]),
|
||||
'm')
|
||||
self.assertEqual(m.text, 'mt')
|
||||
|
||||
def test_name_replace(self):
|
||||
game = paragraphs.Book()
|
||||
game.add(paragraphs.Paragraph('foo'))
|
||||
shuffled = game.shuffle()
|
||||
self.assertEqual(
|
||||
'a1b',
|
||||
paragraphs.paragraph_refs_format('a%(foo)nb', [], shuffled))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
76
test_sections.py
Executable file
76
test_sections.py
Executable file
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env python2.5
|
||||
|
||||
import unittest
|
||||
from unittest import TestCase
|
||||
|
||||
import sections
|
||||
|
||||
class TestSection(TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_create(self):
|
||||
p = sections.Section("foo")
|
||||
self.assertEqual(p.name, "foo")
|
||||
|
||||
class TestBook(TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_create(self):
|
||||
g = sections.Book()
|
||||
self.assertEqual(g.sections, [])
|
||||
self.assertEqual(g.nr_sections, {})
|
||||
self.assertEqual(g.max, 0)
|
||||
|
||||
def test_old_big_mess(self):
|
||||
"""This was tested using print statements in the old
|
||||
sections __main__. Not pretty. Keeping it here anyway
|
||||
as long as it doesn't cause too many problems to update."""
|
||||
from sections import section_refs_format
|
||||
g = sections.Book()
|
||||
c = sections.Section("c", "aaac")
|
||||
g.add(sections.Section("a", "aaa"))
|
||||
g.add(sections.Section("b", "aaab"))
|
||||
g.add(c)
|
||||
g.add(sections.Section("d", "aaad"), 22)
|
||||
g.add(sections.Section("e", "aaae"), 1)
|
||||
g.add(sections.Section("f", "aaaf",
|
||||
[sections.SectionItem("fff")]))
|
||||
g.add(sections.Section("g", "aaag",
|
||||
[sections.SectionItem("ggg", "G")]))
|
||||
m = sections.Section("m")
|
||||
m.addtext("m")
|
||||
m.addtext("t")
|
||||
g.add(m)
|
||||
shuffled = g.shuffle()
|
||||
|
||||
self.assertEqual(len(shuffled.as_list), 23)
|
||||
self.assertEqual(section_refs_format('abc', []), 'abc')
|
||||
self.assertEqual(section_refs_format('abc%%z', []), 'abc%z')
|
||||
self.assertEqual(section_refs_format(
|
||||
'abc%sx',
|
||||
[sections.Section("p1", "111")]),
|
||||
'abcp1x')
|
||||
self.assertEqual(section_refs_format(
|
||||
'abc%ry', [sections.Section("p2", "222")]),
|
||||
"abc'p2'y")
|
||||
self.assertEqual(section_refs_format('%%a%nbc%su', ["f", c],
|
||||
shuffled),
|
||||
'%%a%dbccu' % shuffled.name_to_nr["f"])
|
||||
self.assertEqual(section_refs_format('abc%nu', ["c"], shuffled),
|
||||
'abc%du' % shuffled.name_to_nr["c"])
|
||||
self.assertEqual(section_refs_format('%s', [m]),
|
||||
'm')
|
||||
self.assertEqual(m.text, 'mt')
|
||||
|
||||
def test_name_replace(self):
|
||||
game = sections.Book()
|
||||
game.add(sections.Section('foo'))
|
||||
shuffled = game.shuffle()
|
||||
self.assertEqual(
|
||||
'a1b',
|
||||
sections.section_refs_format('a%(foo)nb', [], shuffled))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
13
todo.org
13
todo.org
|
@ -4,20 +4,19 @@
|
|||
- [X] LaTeX output
|
||||
- [X] RTF output
|
||||
- [X] HTML output
|
||||
- [ ] Save paragraph-number mapping and reuse automatically
|
||||
- [ ] Add support for custom begin/end document templates.
|
||||
- [ ] Save section-number mapping and reuse automatically
|
||||
- [ ] Add support for custom document templates.
|
||||
Fixed names and/or command-line options.
|
||||
- [ ] Add paragraph links in LaTeX output.
|
||||
- [ ] Add section links in LaTeX output.
|
||||
- [ ] Debug HTML output
|
||||
- [ ] Inventory (take, drop, check)
|
||||
- [ ] Codewords/Sightings (set, check, clean)
|
||||
- [ ] Skill checks? Or is this higher level? No?
|
||||
- [ ] More formatting possibilities in paragraphs
|
||||
- [ ] More formatting possibilities in sections
|
||||
Look at existing gamebooks to get ideas.
|
||||
- [ ] Prettier LaTeX output
|
||||
Look at how some existing gamebooks are formatted.
|
||||
- [ ] Command-line flag to set max paragraph number to use
|
||||
- [ ] Dummy paragraphs
|
||||
- [ ] Command-line flag to set max section number to use
|
||||
- [ ] Dummy sections
|
||||
- [ ] Combat? Or is this higher level?
|
||||
- [ ] Document Gamebook JSON format.
|
||||
- [ ] Higher level text-language for Gamebooks
|
||||
|
|
Loading…
Reference in a new issue