Skip to content

Instantly share code, notes, and snippets.

@boxmein
Last active August 29, 2015 14:07
Show Gist options
  • Save boxmein/f48aad2bd2a59ee5c8f7 to your computer and use it in GitHub Desktop.
Save boxmein/f48aad2bd2a59ee5c8f7 to your computer and use it in GitHub Desktop.
A script that generates a JSON data structure based on simtr/The-Powder-Toy element source codes. Also decorates some definitions to make them prettier/more wiki friendly.
#!/usr/bin/env ruby
# usage: ruby generate.rb -d <directory for src/simulation/elements/*>
require 'json'
require 'optparse'
# Descriptive method name award 2014
# dir :: String -- directory to look for element source code in
# outfile :: IO -- output to this writable object (.puts needs to exist)
# opts[:clean] :: Boolean -- clean up mismatches? defaults to true
# opts[:post_process] :: Boolean -- decorate some values to prettier versions? def true
# opts[:pretty] :: Boolean -- output JSON as prettyprinted
def do_the_thing dir, outfile, opts
# Matches name = value; with \w+?
rx = /^\s*(\w+?)\s*=\s*(.+?);$/
# Contains all elements listed
elements = []
Dir.glob (dir + '/*.cpp') do |filename|
puts "------\nelement: #{filename}\n------" if $verbose
el = {}
open(filename, 'r').each_line do |line|
if line =~ rx
el[$1] = $2
puts "found: #{$1} = #{$2}" if $verbose
end
end
elements << el
end
# Clean up the cruft. Don't need to turn that off, riiiiight?
elements = clean_cruft elements if opts[:clean]
# Process elements, or keep real values?
elements = post_process elements if opts[:post_process]
# Output time! Come and grab your friends!
if opts[:pretty]
outfile.puts JSON.pretty_generate elements
else
outfile.puts JSON.generate elements
end
end
def clean_cruft els
#
# Filter out invalid cruft that sometimes gets caught
#
valid_properties = %w[Identifier Name Colour MenuVisible MenuSection Enabled
Advection AirDrag AirLoss Loss Collision Gravity Diffusion HotAir
Flammable Explosive Meltable Hardness Weight Temperature HeatConduct
Description State Properties LowPressure LowPressureTransition HighPressure
HighPressureTransition LowTemperature LowTemperatureTransition
HighTemperature HighTemperatureTransition Update Graphics]
els.map do |a|
a.select do |k, _| valid_properties.include? k end
end
end
# Change some of the element values to suit the wiki better.
# Clear up string cruft, etc.
# Arguably one of the most exciting functions here :D
def post_process els
# Key = property name
# Value = [regex, substitution]
# Match value via regex, replace with substitution. Can use capture groups,
# the values will be replaced from \1..\9 to the captured values $1..$0-9
# If substitution is :skip, then remove key and value from outputted hash
# If substitution is a proc, pass value, key, regex to proc and expect
# the new value. You can return :skip, too!
substitutions = {
# Make colors into CSS colors
"Colour" => [/PIXPACK\(0x([0-9A-Fa-f]{6})\)/, '#\\1'],
# Update/graphics functions don't need pointers
"Update" => [/&.+::update/, :skip],
"Graphics" => [/&.+::graphics/, :skip],
# A lack of transitions can be implicit
"LowPressure" => [/IPL/, :skip],
"HighPressure" => [/IPH/, :skip],
"LowTemperature" => [/ITL/, :skip],
"HighTemperature" => [/ITH/, :skip],
"LowPressureTransition" => [/NT/, :skip],
"HighPressureTransition" => [/NT/, :skip],
"LowTemperatureTransition" => [/NT/, :skip],
"HighTemperatureTransition" => [/NT/, :skip],
# Unquote descriptions and names and identifiers
"Name" => [/^"|"$/, ''],
"Description" => [/^"|"$/, ''],
"Identifier" => [/^"|"$/, ''],
# Turn a state definition into a name and number
"State" => [nil, Proc.new do |val|
"#{STATES[val]} (#{val})"
end],
# Also with the menu sections
"MenuSection" => [nil, Proc.new do |val|
"#{MENU_SECTIONS[val]} (#{val})"
end],
# And with types
"Type" => [nil, Proc.new do |val|
"#{TYPES[val]} (#{val})"
end]
}
els.map! do |a|
substitutions.each_pair do |k, v|
if a[k]
rx, template = v
if template == :skip
a.delete k
elsif template.is_a? Proc
a[k] = template.call a[k], k, rx
# Returning :skip deletes the key
a.delete k if a[k] == :skip
else
a[k] = a[k].gsub rx, template
end
end
end
a
end
els
end
# All of the options passed to the thing
# Most can be set via cmdline options
opts = {
# Cleanup false parameters
clean: true,
# Post-process parameters into a better format
post_process: true,
# Root for all element files
root: "",
# Pretty-print JSON
pretty: false,
# Output file
outfile: nil
}
#
# Command-line interface!
#
OptionParser.new do |parser|
parser.banner = "Generate JSON from the source codes of all the elements.\n"
"If two elements share a name, the last one wins.\n"
"Usage: ruby #{$0} -d <dir>"
"List all options with --help."
parser.on("-d", "--dir DIR",
"Element source directory. blah/src/simulation/elements") do |dir|
raise "Directory doesn't exist" unless Dir.exist? dir
opts[:root] = dir
end
parser.on("-o", "--out [FILE]",
"Output to a file. Any file handle works here, really.") do |f|
opts[:outfile] = open f, 'w'
end
parser.on("-v", "--verbose",
"Become super-loud. Great for logging.") do
$verbose = true
end
parser.on("--pretty", "Pretty-print the JSON.") do
opts[:pretty] = true
end
parser.on("--no-clean", "Don't clean up invalid property matches.") do
opts[:clean] = false
end
parser.on("--no-post-process", "Don't post-process properties to a more "\
"digestible form.") do
opts[:post_process] = false
end
end.parse!
#
# Data
#
MENU_SECTIONS = {
"SC_WALL" => "Walls (0)",
"SC_ELEC" => "Electronics (1)",
"SC_POWERED" => "Powered Elements (2)",
"SC_SENSOR" => "Sensors (3)",
"SC_FORCE" => "Force Elements (4)",
"SC_EXPLOSIVE" => "Explosives (5)",
"SC_GAS" => "Gasses (6)",
"SC_LIQUID" => "Liquids (7)",
"SC_POWDERS" => "Powders (8)",
"SC_SOLIDS" => "Solids (9)",
"SC_NUCLEAR" => "Radioactive (10)",
"SC_SPECIAL" => "Special (11)",
"SC_LIFE" => "Life (12)",
"SC_TOOL" => "Tools (13)",
"SC_DECO" => "Decorations (14)",
"SC_CRACKER" => "Shenanigans (15)",
"SC_CRACKER2" => "Shenanigans (16)"
}
STATES = {
"ST_SOLID" => "Solid",
"ST_LIQUID" => "Liquid",
"ST_GAS" => "Gas",
"ST_ENERGY" => "Energy",
"ST_NONE" => "None"
}
TYPES = {
"TYPE_PART" => "Particle",
"TYPE_SOLID" => "Solid",
"TYPE_ENERGY" => "Energy",
"TYPE_LIQUID" => "Liquid",
"TYPE_GAS" => "Gas"
}
abort "No directory specified, aborting! (how did you get here?)" unless opts[:root]
do_the_thing opts[:root], (opts[:outfile] || STDOUT), opts
# TPT Generator Thing
# Generates JSON data from element source files, with the purpose to describe
# said element. You can use the data wherever.
#
# Usage is simple, just run ruby $0 --help for it to self-describe itself.
#
# boxmein 2014, MIT licensed <3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment