logicle 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +13 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +8 -0
- data/README.md +43 -0
- data/Rakefile +11 -0
- data/bin/logicle +59 -0
- data/examples/simple.tgf +14 -0
- data/examples/simple_solved.tgf +14 -0
- data/lib/logicle.rb +20 -0
- data/lib/logicle/digraph.rb +53 -0
- data/lib/logicle/logicle.rb +3 -0
- data/lib/logicle/node.rb +65 -0
- data/lib/logicle/simulator.rb +35 -0
- data/lib/logicle/tgf_reader.rb +52 -0
- data/lib/logicle/tgf_writer.rb +38 -0
- data/lib/logicle/version.rb +3 -0
- data/logicle.gemspec +23 -0
- data/test/digraph_test.rb +34 -0
- data/test/logicle_test.rb +8 -0
- data/test/node_test.rb +92 -0
- data/test/simple.tgf +14 -0
- data/test/test_helper.rb +40 -0
- data/test/tgf_reader_test.rb +46 -0
- metadata +71 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Academic Exercise: Digital Logic Simulator
|
2
|
+
|
3
|
+
_written by Gregory Brown for Mendicant University core skills session #9_
|
4
|
+
|
5
|
+
I had a lot of fun coming up with the ["All Wired Up" PuzzleNode problem](http://puzzlenode.com/puzzles/18)
|
6
|
+
that was part of this session's entrance
|
7
|
+
exam. At the time I was writing it, I had thought about building a more
|
8
|
+
generally useful tool for playing around with simulated digital circuits,
|
9
|
+
but then I quickly got too busy to look into it further.
|
10
|
+
|
11
|
+
Then, I started playing Minecraft and my interest in circuitry simulations was
|
12
|
+
reignited! The game has a [full circuitry system](http://www.minecraftwiki.net/wiki/Redstone_circuits) for doing all sorts of fun logical
|
13
|
+
processing based on the most primitive inputs. But Minecraft is much better
|
14
|
+
as a game than as a learning environment, and it can be a very frustrating
|
15
|
+
and tedious environment to try to study circuitry in. I want a tool that
|
16
|
+
doesn't make me afraid of being attacked by zombies while I build my logic
|
17
|
+
gates!
|
18
|
+
|
19
|
+
In this exercise, I would like you to build a tool that makes exploring
|
20
|
+
digital circuitry fun and interesting. The summary below provides some
|
21
|
+
suggestions on what things in particular I have in mind, but everyone
|
22
|
+
should be able to come up with their own unique take on the idea without
|
23
|
+
too much overlap.
|
24
|
+
|
25
|
+
## Exercise Summary
|
26
|
+
|
27
|
+
- You should create a tool that makes exploring and learning about
|
28
|
+
logical circuitry fun and interesting.
|
29
|
+
- You can use the "All Wired Up" problem and MineCraft's Redstone circuits
|
30
|
+
as an inspiration, but be sure to have some fresh ideas of your own.
|
31
|
+
- You should be able to build modular gates that can be re-used in other circuits.
|
32
|
+
- The way you handle input and output visualization is completely up to
|
33
|
+
you
|
34
|
+
- Your tool should be easy to work with for someone who wants to explore circuitry but is not necessarily a programmer.
|
35
|
+
- It should be possible to assign inputs, evaluate the circuits, and then inspect their outputs.
|
36
|
+
- Some sort of friendly debugging process and tools would be nice to have.
|
37
|
+
|
38
|
+
## Submission Guidelines
|
39
|
+
|
40
|
+
If you plan to work on this exercise, you should fork this repository
|
41
|
+
and push code early and often during the course. The course
|
42
|
+
guidelines PDF explains the submission process in detail, but please
|
43
|
+
contact an instructor if you have any questions.
|
data/Rakefile
ADDED
data/bin/logicle
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "logicle"
|
4
|
+
|
5
|
+
|
6
|
+
USAGE_MESSAGE = "logicle <CIRCUIT FILE TO SOLVE> "
|
7
|
+
|
8
|
+
|
9
|
+
# Exit with usage message if user forgets to provide an input file.
|
10
|
+
Kernel.abort(USAGE_MESSAGE) if ARGV.length == 0
|
11
|
+
|
12
|
+
|
13
|
+
# Capture inputs from the command line.
|
14
|
+
circuit_file = ARGV.shift
|
15
|
+
|
16
|
+
|
17
|
+
# Instantiate the Logicle::Simulator instance.
|
18
|
+
simulator = Logicle::Simulator.new(circuit_file)
|
19
|
+
puts "Loaded main circuit from file: #{ circuit_file }"
|
20
|
+
|
21
|
+
|
22
|
+
# Prompt the user to enter states for each of the switch inputs.
|
23
|
+
switches = simulator.inputs
|
24
|
+
puts "The circuit to be solved requires #{ switches.count } input values (on/off)."
|
25
|
+
|
26
|
+
switches.each_with_index do |switch, index|
|
27
|
+
$stdout.printf("Switch %d on? (Y/n):>> ", index + 1)
|
28
|
+
input = $stdin.gets.chomp.downcase.chars.first
|
29
|
+
case input
|
30
|
+
when "y"
|
31
|
+
switch.type = :on
|
32
|
+
when "n"
|
33
|
+
switch.type = :off
|
34
|
+
else
|
35
|
+
puts "Invalid input!"
|
36
|
+
redo
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Evaluate the circuit.
|
42
|
+
simulator.evaluate
|
43
|
+
bulbs = simulator.outputs
|
44
|
+
|
45
|
+
|
46
|
+
# Print out the bulb states.
|
47
|
+
bulbs.each_with_index do |bulb, i|
|
48
|
+
puts "Bulb \##{ i + 1 }: #{ bulb.state ? "on" : "off" }"
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# Ask the user to save the result file.
|
53
|
+
$stdout.write "Save output? (y/N): "
|
54
|
+
save_output_flag = $stdin.gets.chomp
|
55
|
+
|
56
|
+
if save_output_flag =~ /\Ay/i
|
57
|
+
output_filename = File.realdirpath(circuit_file).sub(/\.tgf\Z/, "_solved.tgf")
|
58
|
+
simulator.save(output_filename)
|
59
|
+
end
|
data/examples/simple.tgf
ADDED
data/lib/logicle.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "logicle/version"
|
2
|
+
require_relative "logicle/logicle"
|
3
|
+
require_relative "logicle/simulator"
|
4
|
+
require_relative "logicle/tgf_reader"
|
5
|
+
require_relative "logicle/tgf_writer"
|
6
|
+
|
7
|
+
require_relative "logicle/node"
|
8
|
+
require_relative "logicle/digraph"
|
9
|
+
|
10
|
+
module Logicle
|
11
|
+
|
12
|
+
# Raised when trying to create an edge in the graph to or from an unknown node
|
13
|
+
class UnknownNodeError < StandardError; end
|
14
|
+
|
15
|
+
# Raised in case of error while reading an input file
|
16
|
+
class ParseError < StandardError; end
|
17
|
+
|
18
|
+
# Raised when trying to create a node with an unknown type.
|
19
|
+
class UnknownNodeTypeError < StandardError; end
|
20
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Logicle
|
2
|
+
class Digraph
|
3
|
+
attr_reader :nodes, :edges
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@nodes, @edges = {}, {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def evaluate
|
10
|
+
outputs.each_value do |output|
|
11
|
+
output.state
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def inputs
|
16
|
+
@nodes.select { |id, node| node.switch? }
|
17
|
+
end
|
18
|
+
|
19
|
+
def outputs
|
20
|
+
@nodes.select { |id, node| node.bulb? }
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_node(id, node_type)
|
24
|
+
@nodes[id] = Node.new(id, node_type)
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_edge(start_id, end_id)
|
28
|
+
start_node, end_node = @nodes[start_id], @nodes[end_id]
|
29
|
+
|
30
|
+
if start_node && end_node
|
31
|
+
end_node.append_input(start_node)
|
32
|
+
@edges[start_id] = end_id
|
33
|
+
true
|
34
|
+
else
|
35
|
+
raise_unknown_nodes(start_id => start_node, end_id => end_node)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def raise_unknown_nodes(node_map={})
|
41
|
+
bad_node_ids = node_map.select { |id, node| node.nil? }.keys
|
42
|
+
|
43
|
+
case bad_node_ids.count
|
44
|
+
when 0
|
45
|
+
return
|
46
|
+
when 1
|
47
|
+
raise UnknownNodeError, "Invalid node id: #{ bad_node_ids[0] }"
|
48
|
+
else
|
49
|
+
raise UnknownNodeError, "Invalid node ids: #{ bad_node_ids.join(", ") }"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/logicle/node.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module Logicle
|
2
|
+
class Node
|
3
|
+
LOGIC_OPERATIONS = {
|
4
|
+
switch: Proc.new { |args| nil },
|
5
|
+
bulb: Proc.new { |args| args[0].state },
|
6
|
+
on: Proc.new { |args| true },
|
7
|
+
off: Proc.new { |args| false },
|
8
|
+
not: Proc.new { |args| !args[0].state },
|
9
|
+
and: Proc.new { |args| args[0].state & args[1].state },
|
10
|
+
or: Proc.new { |args| args[0].state | args[1].state },
|
11
|
+
nand: Proc.new { |args| !args[0].state | !args[1].state },
|
12
|
+
nor: Proc.new { |args| !args[0].state & !args[1].state },
|
13
|
+
xor: Proc.new { |args| args[0].state ^ args[1].state },
|
14
|
+
xnor: Proc.new { |args| !(args[0].state ^ args[1].state) }
|
15
|
+
}
|
16
|
+
|
17
|
+
attr_reader :id, :type, :inputs
|
18
|
+
|
19
|
+
def initialize(id, type)
|
20
|
+
@id = id
|
21
|
+
@type = validate_type(type)
|
22
|
+
@inputs = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def bulb?
|
26
|
+
@type == :bulb
|
27
|
+
end
|
28
|
+
|
29
|
+
def switch?
|
30
|
+
@type == :switch
|
31
|
+
end
|
32
|
+
|
33
|
+
def type=(new_type)
|
34
|
+
@type = validate_type(new_type)
|
35
|
+
end
|
36
|
+
|
37
|
+
def append_inputs(*nodes)
|
38
|
+
@inputs += nodes
|
39
|
+
end
|
40
|
+
alias_method :append_input, :append_inputs
|
41
|
+
|
42
|
+
def clear_inputs
|
43
|
+
@inputs = []
|
44
|
+
@state = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def state
|
48
|
+
@state ||= evaluate
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def validate_type(type)
|
53
|
+
if LOGIC_OPERATIONS.keys.include?(type)
|
54
|
+
type
|
55
|
+
else
|
56
|
+
raise UnknownNodeTypeError, "Unknown node type: '#{ type }'"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def evaluate
|
61
|
+
operation = LOGIC_OPERATIONS[@type]
|
62
|
+
operation.call(@inputs)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Logicle
|
2
|
+
class Simulator
|
3
|
+
attr_reader :circuit
|
4
|
+
|
5
|
+
def initialize(*circuits)
|
6
|
+
@circuit = load(circuits.shift)
|
7
|
+
unless circuits.empty?
|
8
|
+
@sub_circuits = circuits.map { |circuit_file| load(circuit_file) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def inputs
|
13
|
+
@circuit.inputs.values
|
14
|
+
end
|
15
|
+
|
16
|
+
def outputs
|
17
|
+
@circuit.outputs.values
|
18
|
+
end
|
19
|
+
|
20
|
+
def evaluate
|
21
|
+
@circuit.evaluate
|
22
|
+
end
|
23
|
+
|
24
|
+
def save(output_file)
|
25
|
+
writer = TgfWriter.new(output_file, @circuit)
|
26
|
+
writer.write
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def load(circuit_file)
|
31
|
+
reader = TgfReader.new(circuit_file)
|
32
|
+
@circuit = reader.parse
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Logicle
|
2
|
+
class TgfReader
|
3
|
+
def initialize(input)
|
4
|
+
if File.exists?(input)
|
5
|
+
@contents = File.readlines(input) # read file for TGF content
|
6
|
+
else
|
7
|
+
@contents = input.lines # use string param as TGF content
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@circuit = Digraph.new
|
13
|
+
still_reading_nodes = true
|
14
|
+
|
15
|
+
@contents.each do |line|
|
16
|
+
line.chomp!
|
17
|
+
|
18
|
+
if line =~ /\A#/
|
19
|
+
still_reading_nodes = false
|
20
|
+
elsif still_reading_nodes
|
21
|
+
node_directive(line)
|
22
|
+
else
|
23
|
+
edge_directive(line)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
@circuit
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def node_directive(text)
|
32
|
+
if text =~ /\A(\d+)\s+(.*)\Z/
|
33
|
+
id, label = $1, $2
|
34
|
+
@circuit.add_node(id, label.downcase.to_sym)
|
35
|
+
else
|
36
|
+
raise ParseError, "Unable to parse TGF node directive: '#{ text }'"
|
37
|
+
end
|
38
|
+
rescue UnknownNodeTypeError
|
39
|
+
raise ParseError, "Unknown node type in directive: '#{ label }'"
|
40
|
+
end
|
41
|
+
|
42
|
+
def edge_directive(text)
|
43
|
+
if text =~ /\A(\d+)\s+(\d+)\Z/
|
44
|
+
start, finish = $1, $2
|
45
|
+
@circuit.add_edge(start, finish)
|
46
|
+
else
|
47
|
+
raise ParseError, "Unable to parse TGF edge directive: '#{ text }'"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Logicle
|
2
|
+
class TgfWriter
|
3
|
+
def initialize(filename, circuit)
|
4
|
+
@output_file = File.open(filename, "w")
|
5
|
+
@circuit = circuit
|
6
|
+
end
|
7
|
+
|
8
|
+
def write
|
9
|
+
@circuit.nodes.values.each { |node| write_node(node) }
|
10
|
+
|
11
|
+
write_separator
|
12
|
+
|
13
|
+
@circuit.edges.each { |start, finish| write_edge(start, finish) }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def write_node(node)
|
18
|
+
id = node.id
|
19
|
+
type = case node.type
|
20
|
+
when :on, :off
|
21
|
+
"SWITCH"
|
22
|
+
else
|
23
|
+
node.type.to_s.upcase
|
24
|
+
end
|
25
|
+
state = node.state ? "ON" : "OFF"
|
26
|
+
|
27
|
+
@output_file.puts "#{ id } #{ type } (#{ state })"
|
28
|
+
end
|
29
|
+
|
30
|
+
def write_separator
|
31
|
+
@output_file.puts "#"
|
32
|
+
end
|
33
|
+
|
34
|
+
def write_edge(start_id, end_id)
|
35
|
+
@output_file.puts "#{ start_id } #{ end_id }"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/logicle.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "logicle/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "logicle"
|
6
|
+
s.version = Logicle::VERSION
|
7
|
+
s.authors = ["Chris kottom"]
|
8
|
+
s.email = "chris@chriskottom.com"
|
9
|
+
s.homepage = ""
|
10
|
+
s.summary = %q{A simulator for playing with digital circuit logic}
|
11
|
+
s.description = %q{Logicle is a simulator for testing and solving simple circuits composed of basic logical elements. The program reads and writes its inputs and outputs using the GML protocol.}
|
12
|
+
|
13
|
+
s.rubyforge_project = "logicle"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = ["logicle"]
|
18
|
+
s.require_paths = ["lib", "vendor"]
|
19
|
+
|
20
|
+
# specify any dependencies here; for example:
|
21
|
+
# s.add_development_dependency "rspec"
|
22
|
+
# s.add_runtime_dependency "rest-client"
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
|
6
|
+
class DigraphTest < Test::Unit::TestCase
|
7
|
+
include TestHelper
|
8
|
+
|
9
|
+
def test_add_node
|
10
|
+
start_node_count = digraph.instance_variable_get(:@nodes).count
|
11
|
+
digraph.add_node(1, :and)
|
12
|
+
assert_equal(start_node_count + 1,
|
13
|
+
digraph.instance_variable_get(:@nodes).count)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_add_edge
|
17
|
+
start_edge_count = digraph.instance_variable_get(:@edges).count
|
18
|
+
digraph.add_node(1, :and)
|
19
|
+
digraph.add_node(2, :or)
|
20
|
+
digraph.add_edge(1, 2)
|
21
|
+
assert_equal(start_edge_count + 1,
|
22
|
+
digraph.instance_variable_get(:@edges).count)
|
23
|
+
|
24
|
+
nodes = digraph.instance_variable_get(:@nodes)
|
25
|
+
assert(nodes[2].inputs.include?(nodes[1]))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_add_edge_with_unknown_node
|
29
|
+
assert_raises(Logicle::UnknownNodeError) do
|
30
|
+
digraph.add_node(1, :and)
|
31
|
+
digraph.add_edge(1, 2)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/test/node_test.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
|
6
|
+
class NodeTest < Test::Unit::TestCase
|
7
|
+
include TestHelper
|
8
|
+
|
9
|
+
def test_on_node
|
10
|
+
assert_equal(true, node(:on).state)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_off_node
|
14
|
+
assert_equal(false, node(:off).state)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_not_node
|
18
|
+
not_node = node(:not)
|
19
|
+
on_node = node(:on)
|
20
|
+
off_node = node(:off)
|
21
|
+
|
22
|
+
assert_node_state_for_inputs(false, not_node, on_node)
|
23
|
+
assert_node_state_for_inputs(true, not_node, off_node)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_and_node
|
27
|
+
and_node = node(:and)
|
28
|
+
on_node = node(:on)
|
29
|
+
off_node = node(:off)
|
30
|
+
|
31
|
+
assert_node_state_for_inputs(true, and_node, on_node, on_node)
|
32
|
+
assert_node_state_for_inputs(false, and_node, on_node, off_node)
|
33
|
+
assert_node_state_for_inputs(false, and_node, off_node, on_node)
|
34
|
+
assert_node_state_for_inputs(false, and_node, off_node, off_node)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_or_node
|
38
|
+
or_node = node(:or)
|
39
|
+
on_node = node(:on)
|
40
|
+
off_node = node(:off)
|
41
|
+
|
42
|
+
assert_node_state_for_inputs(true, or_node, on_node, on_node)
|
43
|
+
assert_node_state_for_inputs(true, or_node, on_node, off_node)
|
44
|
+
assert_node_state_for_inputs(true, or_node, off_node, on_node)
|
45
|
+
assert_node_state_for_inputs(false, or_node, off_node, off_node)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_nand_node
|
49
|
+
nand_node = node(:nand)
|
50
|
+
on_node = node(:on)
|
51
|
+
off_node = node(:off)
|
52
|
+
|
53
|
+
assert_node_state_for_inputs(false, nand_node, on_node, on_node)
|
54
|
+
assert_node_state_for_inputs(true, nand_node, on_node, off_node)
|
55
|
+
assert_node_state_for_inputs(true, nand_node, off_node, on_node)
|
56
|
+
assert_node_state_for_inputs(true, nand_node, off_node, off_node)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_nor_node
|
60
|
+
nor_node = node(:nor)
|
61
|
+
on_node = node(:on)
|
62
|
+
off_node = node(:off)
|
63
|
+
|
64
|
+
assert_node_state_for_inputs(false, nor_node, on_node, on_node)
|
65
|
+
assert_node_state_for_inputs(false, nor_node, on_node, off_node)
|
66
|
+
assert_node_state_for_inputs(false, nor_node, off_node, on_node)
|
67
|
+
assert_node_state_for_inputs(true, nor_node, off_node, off_node)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_xor_node
|
71
|
+
xor_node = node(:xor)
|
72
|
+
on_node = node(:on)
|
73
|
+
off_node = node(:off)
|
74
|
+
|
75
|
+
assert_node_state_for_inputs(false, xor_node, on_node, on_node)
|
76
|
+
assert_node_state_for_inputs(true, xor_node, on_node, off_node)
|
77
|
+
assert_node_state_for_inputs(true, xor_node, off_node, on_node)
|
78
|
+
assert_node_state_for_inputs(false, xor_node, off_node, off_node)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_xnor_node
|
82
|
+
xnor_node = node(:xnor)
|
83
|
+
on_node = node(:on)
|
84
|
+
off_node = node(:off)
|
85
|
+
|
86
|
+
assert_node_state_for_inputs(true, xnor_node, on_node, on_node)
|
87
|
+
assert_node_state_for_inputs(false, xnor_node, on_node, off_node)
|
88
|
+
assert_node_state_for_inputs(false, xnor_node, off_node, on_node)
|
89
|
+
assert_node_state_for_inputs(true, xnor_node, off_node, off_node)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
data/test/simple.tgf
ADDED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative "../lib/logicle.rb"
|
2
|
+
|
3
|
+
module TestHelper
|
4
|
+
def node(type, id=1)
|
5
|
+
Logicle::Node.new(id, type)
|
6
|
+
end
|
7
|
+
|
8
|
+
def digraph
|
9
|
+
@digraph ||= Logicle::Digraph.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def assert_node_state_for_inputs(value, target_node, *inputs)
|
13
|
+
target_node.clear_inputs
|
14
|
+
target_node.append_inputs(*inputs)
|
15
|
+
assert_equal(value, target_node.state)
|
16
|
+
end
|
17
|
+
|
18
|
+
def tgf_reader(input=tgf_content)
|
19
|
+
@tgf_reader ||= Logicle::TgfReader.new(input)
|
20
|
+
end
|
21
|
+
|
22
|
+
def tgf_content
|
23
|
+
@tgf_content ||= <<-TGF.gsub(/^\s+/, "")
|
24
|
+
1 NOT
|
25
|
+
2 AND
|
26
|
+
3 OR
|
27
|
+
4 SWITCH
|
28
|
+
5 SWITCH
|
29
|
+
6 SWITCH
|
30
|
+
7 BULB
|
31
|
+
#
|
32
|
+
4 1
|
33
|
+
5 2
|
34
|
+
6 2
|
35
|
+
2 3
|
36
|
+
1 3
|
37
|
+
3 7
|
38
|
+
TGF
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
|
6
|
+
class TgfReaderTest < Test::Unit::TestCase
|
7
|
+
include TestHelper
|
8
|
+
|
9
|
+
def test_initialization_with_filename
|
10
|
+
filename = File.join(File.dirname(__FILE__), "simple.tgf")
|
11
|
+
file_content = File.readlines(filename)
|
12
|
+
|
13
|
+
assert_equal(file_content,
|
14
|
+
tgf_reader(filename).instance_variable_get(:@contents))
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_initialization_with_string
|
18
|
+
assert_equal(tgf_content.lines.to_a,
|
19
|
+
tgf_reader.instance_variable_get(:@contents).to_a)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_parsing
|
23
|
+
circuit = tgf_reader.parse
|
24
|
+
|
25
|
+
node_lines, edge_lines = tgf_content.split(/#\s*\n/)
|
26
|
+
|
27
|
+
assert_equal(node_lines.chomp.lines.count,
|
28
|
+
circuit.instance_variable_get(:@nodes).count)
|
29
|
+
assert_equal(edge_lines.lines.count,
|
30
|
+
circuit.instance_variable_get(:@edges).count)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_parsing_with_bad_node_type
|
34
|
+
content = "0 FOO\n" << tgf_content
|
35
|
+
assert_raises(Logicle::ParseError) do
|
36
|
+
tgf_reader(content).parse
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_parsing_with_bad_edge
|
41
|
+
content = tgf_content << "\n10000 100001"
|
42
|
+
assert_raises(Logicle::ParseError) do
|
43
|
+
tgf_reader(content).parse
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logicle
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chris kottom
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-24 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: Logicle is a simulator for testing and solving simple circuits composed
|
15
|
+
of basic logical elements. The program reads and writes its inputs and outputs using
|
16
|
+
the GML protocol.
|
17
|
+
email: chris@chriskottom.com
|
18
|
+
executables:
|
19
|
+
- logicle
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- .gitignore
|
24
|
+
- Gemfile
|
25
|
+
- Gemfile.lock
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- bin/logicle
|
29
|
+
- examples/simple.tgf
|
30
|
+
- examples/simple_solved.tgf
|
31
|
+
- lib/logicle.rb
|
32
|
+
- lib/logicle/digraph.rb
|
33
|
+
- lib/logicle/logicle.rb
|
34
|
+
- lib/logicle/node.rb
|
35
|
+
- lib/logicle/simulator.rb
|
36
|
+
- lib/logicle/tgf_reader.rb
|
37
|
+
- lib/logicle/tgf_writer.rb
|
38
|
+
- lib/logicle/version.rb
|
39
|
+
- logicle.gemspec
|
40
|
+
- test/digraph_test.rb
|
41
|
+
- test/logicle_test.rb
|
42
|
+
- test/node_test.rb
|
43
|
+
- test/simple.tgf
|
44
|
+
- test/test_helper.rb
|
45
|
+
- test/tgf_reader_test.rb
|
46
|
+
homepage: ''
|
47
|
+
licenses: []
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
- vendor
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project: logicle
|
67
|
+
rubygems_version: 1.8.10
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: A simulator for playing with digital circuit logic
|
71
|
+
test_files: []
|