logicle 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|