Utility to Generate a Graphical Tech Tree from techs.ruleset

Various topics about the game, the website, or anything else Freeciv related that doesn't fit elsewhere.
User avatar
vodot
Veteran
Posts: 79
Joined: Thu May 17, 2018 4:54 pm

Utility to Generate a Graphical Tech Tree from techs.ruleset

Postby vodot » Fri Jul 13, 2018 9:56 pm

There used to be a utility would generate a tech tree from a techs.ruleset file. Anyone know where I could find that?

EDIT: found this page, but the tech tree util seems dead: http://freeciv.wikia.com/wiki/Utilities

User avatar
JTN
Elite
Posts: 462
Joined: Wed Jan 30, 2013 12:15 am

Re: Utility to Generate a Graphical Tech Tree from techs.ruleset

Postby JTN » Sat Jul 14, 2018 8:43 am

The TechTree utility linked there is the only one I know of. No idea if it still works; it might well need tweaking.

I've fixed the links on that page to point to files.freeciv.org (where most stuff from download.gna.org ended up), and most of the links now work, including the TechTree one.

madmax
Posts: 36
Joined: Sun Mar 26, 2017 5:34 pm

Re: Utility to Generate a Graphical Tech Tree from techs.ruleset

Postby madmax » Sat Jul 14, 2018 7:58 pm

From the help, it seems it uses as input the output of a server command that is no more.

If you just need a tech tree, without all the options and info that utility had, below is a dirty python script that translates a techs.ruleset into graphviz format. Example usage:

Code: Select all

./techtree.py ~/src/freeciv-2.6/data/classic/techs.ruleset | dot -Tpng > classic.png


Doesn't tell you the effects of the techs, what units, buildings, etc they make available or obsolete, doesn't translate, doesn't support include... You just get the tech requirements graph.

Code: Select all

#!/usr/bin/python3

import sys
import configparser
import re


def freeciv_to_ini(filename):
  with open(filename, 'r') as file:
    clean_line = ''
    for line in file:
      if line == '\n':
        continue
      if line[-2] == '\\':
        clean_line = clean_line + line[:-2]
      else:
        if line[-2] == ';':
          line = line[:-2]
        yield clean_line + line
        clean_line = ''


config = configparser.ConfigParser(interpolation=None)
config.read_file(freeciv_to_ini(sys.argv[1]))
advances = dict()
name_re = re.compile('(_\()?("(\?[^:]*:)?([^"]*"))')

for section in config.sections():
  if section[:8] == 'advance_':
    name = ''
    rule_name = ''
    reqs = list()
    for key, value in config.items(section):
      if key == 'rule_name':
        rule_name = value
      elif key == 'name':
        name = '"' + re.match(name_re, value).group(4)
        if rule_name == '':
          rule_name = name
      elif key[:3] == 'req' and value != '"None"':
        reqs.append(value)

    advances[rule_name] = (name, reqs)

print('digraph tree {')
print('label=', config.get('datafile', 'description'), ';')
for key, value in advances.items():
  print(key, '[label=', value[0], '];')
for key, value in advances.items():
  for req in value[1]:
    print(req, '->', key, ';')
print('}')


Edit: corrected so that translation contexts don't duplicate nodes.
Last edited by madmax on Sun Jul 15, 2018 10:06 am, edited 1 time in total.

User avatar
JTN
Elite
Posts: 462
Joined: Wed Jan 30, 2013 12:15 am

Re: Utility to Generate a Graphical Tech Tree from techs.ruleset

Postby JTN » Sat Jul 14, 2018 10:13 pm

madmax wrote:From the help, it seems it uses as input the output of a server command that is no more.

Looks like the "rulesout" command it relied on was removed from 1.14.

However, the next script on the Utilities page, "Rulesout perl script", looks like it was intended to replace the missing command. Looks like it reads ruleset files. It is likely to need tweaking for ruleset format changes since 2004.

User avatar
vodot
Veteran
Posts: 79
Joined: Thu May 17, 2018 4:54 pm

Re: Utility to Generate a Graphical Tech Tree from techs.ruleset

Postby vodot » Mon Jul 16, 2018 4:07 am

JTN wrote:The TechTree utility linked there is the only one I know of. No idea if it still works; it might well need tweaking.

I've fixed the links on that page to point to files.freeciv.org (where most stuff from download.gna.org ended up), and most of the links now work, including the TechTree one.


Thanks!!

madmax wrote:From the help, it seems it uses as input the output of a server command that is no more.

If you just need a tech tree, without all the options and info that utility had, below is a dirty python script that translates a techs.ruleset into graphviz format. Example usage:

Code: Select all

./techtree.py ~/src/freeciv-2.6/data/classic/techs.ruleset | dot -Tpng > classic.png


Doesn't tell you the effects of the techs, what units, buildings, etc they make available or obsolete, doesn't translate, doesn't support include... You just get the tech requirements graph.

Code: Select all

#!/usr/bin/python3

import sys
import configparser
import re


def freeciv_to_ini(filename):
  with open(filename, 'r') as file:
    clean_line = ''
    for line in file:
      if line == '\n':
        continue
      if line[-2] == '\\':
        clean_line = clean_line + line[:-2]
      else:
        if line[-2] == ';':
          line = line[:-2]
        yield clean_line + line
        clean_line = ''


config = configparser.ConfigParser(interpolation=None)
config.read_file(freeciv_to_ini(sys.argv[1]))
advances = dict()
name_re = re.compile('(_\()?("(\?[^:]*:)?([^"]*"))')

for section in config.sections():
  if section[:8] == 'advance_':
    name = ''
    rule_name = ''
    reqs = list()
    for key, value in config.items(section):
      if key == 'rule_name':
        rule_name = value
      elif key == 'name':
        name = '"' + re.match(name_re, value).group(4)
        if rule_name == '':
          rule_name = name
      elif key[:3] == 'req' and value != '"None"':
        reqs.append(value)

    advances[rule_name] = (name, reqs)

print('digraph tree {')
print('label=', config.get('datafile', 'description'), ';')
for key, value in advances.items():
  print(key, '[label=', value[0], '];')
for key, value in advances.items():
  for req in value[1]:
    print(req, '->', key, ';')
print('}')


Edit: corrected so that translation contexts don't duplicate nodes.


...dude, did you just write this??