2 # (c) Copyright 2013 TJ <hacker@iam.tj>
3 # Licensed on the terms of the GNU General Public License version 3 (see COPYING)
5 # Base2 Runner: Circuit design and management
7 # TODO: module in development, not ready for any use yet
10 import os.path, pygame, engine, library
11 from library import enum
15 """ Models a straight line segment of a complete Trace and any associated Via (through-hole)
16 A segment may only have at most 1 Via
18 The direction is limited to 8 points on the compass
20 The length is an arbitrary multiplier. The game itself will decide how many pixels represent
21 the minimum length of a segment. Suggestion is that the value be the minimum width of cells in
22 the game-map matrix if a tile-based model of the playing area is in use.
25 def __init__(self, length=1, direction=library.directions.E, via=None, via_position=library.positions.NONE):
26 """ default segment runs for one game tile length left-to-right
27 length: number of game 'tiles' this segment covers (default = 1)
28 direction: enum directions (default = East)
29 via: Via (default = None)
30 via_position: enum positions (default = NONE)
33 self.angles = ( 0 , 45 , 90, 135, 180, 225, 270, 315)
35 # references to an attached Via and its position
37 self.via_position = None
39 # ensure all values are legal before doing any assignment
40 if not direction in library.directions.reverse_mapping:
41 # 'direction' is an index enum, so ensure it exists in the enum before using it to select the angle
42 raise IndexError('direction invalid')
43 elif not via_position in library.positions:
44 # 'position' is an enum so ensure it is valid
45 raise IndexError('position invalid')
46 elif via and not isinstance(via, Via):
47 raise TypeError('expecting a Via')
50 self.angle = angles[direction]
52 self.via_position = via_position
54 def set_via(self, via, position):
55 """ link to the related Via
57 position: enum via_pos
59 if not isinstance(via, Via):
60 raise TypeError('expecting a Via')
61 elif not position in library.positions:
62 # FIXME: ensure the line above works for testing position's membership
63 raise IndexError('position invalid')
66 self.via_position = position
68 def set_direction(self, direction):
69 """ set the direction the segment is headed
70 direction: enum directions
72 if not direction in library.directions.reverse_mapping:
73 raise IndexError('direction invalid')
74 self.angle = self.angles(direction)
78 """ Models a single circuit trace made up of multiple segments """
80 def __init__(self, metal=(192, 192, 192), shadow=(64, 64, 64), reflection=(255, 255, 255), segments=None):
81 self.segments = list() # ordered list of one or more TraceSegments
84 # colours['metal'] = (192, 192, 192) # colours of the trace
85 # colours['shadow'] = (64, 64, 64)
86 # colours['reflection'] = (255, 255, 255)
88 # set the colours of the trace to give a 3D raised appearance
89 if metal and check_range(metal): self.colours['metal'] = metal
90 if shadow and check_range(shadow): self.colours['shadow'] = shadow
91 if reflection and check_range(reflection): self.colours['reflection'] = reflection
94 self.segments = TraceSegment()
96 self.segments = segments
98 with self.segments[0]:
100 via_position = library.positions.NONE
102 def check_range(self, colour):
103 """ Ensure a colour value is legal """
104 if not (type(colour) == tuple or type(colour) == int):
105 raise TypeError('colour expeted to be tuple or int')
106 elif not len(colour) == 3:
107 raise ValueError('colour tuples require exactly 3 items (RGB)')
109 if type(colour) == int:
110 return colour >= 0 and colour <= 255
112 return self.check_range(colour[0]) and self.check_range(colour[1]) and self.check_range(colour[2])
114 def draw(self, surface):
115 """ Draw the Trace onto pygame.Surface """
120 """ Models a through-hole otherwise known as a Via """
123 self.trace = None # the Trace this via is attached to
124 self.linked = None # the Via this via links to
128 """ Group of traces all going in the same direction """
130 def __init__(self, buswidth=8):
131 """ Create a new bus consisting of 'buswidth' traces """
132 self.BUSWIDTH_MAX = 32
133 if not (buswidth >= 1 and buswidth <= self.BUSWIDTH_MAX):
134 raise ValueError('bus width out of range')
136 self.width = 8 # default is an 8-bit bus
140 class IntegratedCircuit:
141 """ semiconductor component """
144 self.pins = list() # index = pin number, value = signal name
147 class DualInLine(IntegratedCircuit):
148 """ Dual In-Line Integrated Circuit """
150 def __init__(self, pin_qty):
154 if not (pin_qty > 0 and pin_qty % 2 == 0):
155 raise ValueError('DIL ICs have even number of pins')
157 for p in range(1, pin_qty+1):
158 self.pins.insert(p, library.signals.NC)
162 """ Models a single logic gate """
164 def __init__(self, gate_type, inputs=None):
167 self.gate_type = None
169 if not gate_type in library.gates:
172 self.gate_type = gate_type
175 # default to 2 inputs at logic 0
176 self.inputs = (False, False)
177 elif not (type(inputs) is list and isinstance(inputs[0], bool)):
181 self.input_max = len(self.inputs)
184 def update_state(self):
185 """ Needs to be over-ridden in derived classes """
188 def set_input(self, input, value):
189 if not(input >= 0 and input < self.input_max):
190 raise ValueError("input expected between 0 and" % self.input_max)
192 if not isinstance(value, bool):
193 raise TypeError('Boolean expected')
195 self.inputs[input] = value
198 def get_output(self):
201 def min_inputs(self, inputs, minimum):
202 """ enforce a minimum number of logic inputs in the list """
203 return inputs if len(inputs) >= minimum else (inputs, (False for i in range(0, minimum - len(inputs))))
206 # Derive each of the common logic gate types
209 def update_state(self):
210 self.output = self.inputs[0]
211 for i in range(1, self.input_max):
212 self.output &= self.inputs[i] # AND
216 def __init__(self, inputs=None):
217 Gate.__init__(library.gates.OR, self.min_inputs(inputs, 2))
219 def update_state(self):
220 self.output = self.inputs[0]
221 for i in range (1, self.input_max):
222 self.output |= self.inputs[i] # OR
226 def __init__(self, inputs=None):
227 Gate.__init__(library.gates.XOR, self.min_inputs(inputs, 2))
229 def update_state(self):
230 self.output = self.inputs[0]
231 for i in range (1, self.input_max):
232 self.output ^= self.inputs[i] # XOR
236 def __init__(self, inputs=(False, )):
238 raise ValueError('NOT must have only 1 input')
239 Gate.__init__(library.gates.NOT, inputs)
241 def update_state(self):
242 self.output = ~self.inputs[0] # invert
245 class GateNAND(Gate):
246 def __init__(self, inputs=None):
247 Gate.__init__(library.gates.NAND, self.min_inputs(inputs, 2))
249 def update_state(self):
250 self.output = self.inputs[0]
251 for i in range(1, self.input_max):
252 self.output &= self.inputs[i] # AND
253 self.output = ~self.output # invert
257 def __init__(self, inputs=None):
258 Gate.__init__(library.gates.NOR, self.min_inputs(inputs, 2))
260 def update_state(self):
261 self.output = self.inputs[0]
262 for i in range (1, self.input_max):
263 self.output |= self.inputs[i] # OR
264 self.output = ~self.output # invert
268 """ Models an IC that contains one or more digital logic gates """
270 def __init__(self, gates=()):
273 if not type(gates) is list:
274 raise TypeError('expected list')
275 elif not type(gates[0]) is Gate:
276 raise TypeError('expected Gate')
278 self.gates.append(gates)
281 if not type(gate) is Gate:
282 raise TypeError('expected Gate')
284 self.gates.append(gate)
286 class ICLogicDIL(ICLogic, DualInLine):
287 """ Model of a Logic IC ina DIL package """
288 def __new__(cls, *args):
289 print("ICLoigcDIL.__new__()")
291 def __init__(self, gates=(), pins_qty=14):
292 print('ICLogicDL.__slots__ =',self. __slots__)
293 ICLogic.__init(gates)
294 DualInLine.__init__(pins_qty)
297 class SN7400(ICLogicDIL):
298 """ Model of a Texas Instruments SN7400 quad 2-input NAND gate DIP14 package """
301 # call the super classes constructors
303 for i in range(0, 4):
304 gates.append(GateNAND())
305 ICLogicDIL.__init__(gates, 14)
308 class SN741G00(ICLogicDIL):
309 """ Model of a Texas Instruments SN741G00 single 2-input NAND gate """
312 class TSSOP(IntegratedCircuit):
313 """ Thin Shrink Small Outline Package """
314 # emulate C++/Java classes
315 __slots__ = ['sides', 'pins']
317 def __init__(self, pins):
324 """ Models a digital electronics logic circuit printed on a circuit board """
331 def trace_add(self, trace_list):
335 def component_insert(self, component):
336 """ Adds a component to the circuit """