A couple of days ago I started playing with Emscripten to see if DLS can run in the browser. Apparently it can!
So, I decided to remove the obselete v0.10.1 I was keeping as a demo, since the differences between that and the latest version (0.14) are big.
You can evaluate DLS’ capabilities with the online sandbox from now on. I added a link to it to the Links section on the game’s page on itch.io. In case you decide to purchase DLS in order to run it on your desktop (and/or support further development) and it doesn’t work correctly (i.e. due to an untested Linux distro), I’ll be glad to hear about it and try to fix it.
Finally, note that even though the online sandbox supports all the features of the desktop version, there are no puzzles and the performance isn’t on par with the desktop version (if your browser supports it, I’d recommend running the WebAssembly version).
TL;DR: New version is out; further development is paused for now (only bug fixes and small improvements); making games is hard and it’s really easy to loose focus if you don’t have a plan :)
Two days ago I uploaded v0.14 of DLS on itch.io and GameJolt. This will probably be the last update for a long time, at least in its current form. The reason for this is because I need to figure out where I’m going to take DLS from here.
Before writing anything else, I’d like to thank everyone who took the time to download and play with it, especially those who submitted feedback and those who spent their money on it.
My initial goal was to write a simple logic simulator and then build some kind of game around it (preferably interesting and fun :)). Unfortunately, I didn’t have a concrete plan on the game part from the beginning, so I ended up focusing on the simulator most of the time. The end result is a simulator capable enough to simulate a real CPU, although it’s microcoded (i.e. most of the instruction decoding is implemented using ROMs instead of logic gates) and the simulation speed isn’t really great (only 1.3ms/sec with a 20MHz clock). By the way, I was planning on writing at least one blog post on it but I haven’t found the time yet.
As you probably know, DLS already includes some puzzles for you to solve. The problem with those (as I see it) is that they are either too simple or too complicated. Taking into account the fact that there’s no physical, real world, meaning to the objectives of each level, makes them even more complex. I.e. there’s a sequential level which requires the player to build a 4-bit Serial-in/Parallel-out circuit. In my opinion, it would be more interesting if the output of the circuit was some kind of ASCII message reconstruction shown on an LCD display. The objective of the puzzle would stay (more or less) the same, but the output would have been a bit more interesting.
It’s things like that that really bother me with the current state of the game and I need some time to think about them and decide how to move forward. As far as I can tell, there are 3 different ways to go.
Abandon the game idea completely and focus on the sandbox/simulator.
Keep the same kinds of puzzles and try to make them better and more interesting.
Create a completely different kind of game which will use the simulator as the primary way of achieving certain goals.
Ultimately, I’d like to build no. 3 but I don’t know where to begin and what the bigger picture will be.
No. 1 isn’t much of an option because (as I’ve written in a previous post) I’m not qualified enough to build a better logic simulator. There are many different kinds of logic simulators out there which do things faster and better than DLS. If I decide to take this route there are a lot of things to change and add, and the final product will probably won’t have many things in common with the current version.
No. 2 seems like the best option right now, because I already have the base for it. But in its current state it’s missing the “interesting and fun” part I mentioned earlier.
Having said the above, I will continue working on it in my spare time. I’ll be fixing bugs and trying to make the UI more functional and easier to use. And more importantly, I’ll continue to take notes based on your feedback, hoping that it’ll push me to the right direction :)
Thanks for reading!
PS. Here’s a list of games to keep you busy in the meantime (WARNING: I have no connection with those games or their developers and I haven’t played most of them)
As of yesterday (19 Sept 2016) DLS has its first set of playable levels! 37 to be exact :)
Currently there are 3 different level types. Combinational circuits, sequential circuits and stream processing circuits.
In a combinational circuit level your goal is to build a circuit which, given some specific inputs, will produce the expected outputs combinationally i.e. will react to input changes immediately, without the use of flip flops, registers and clocks. Figure 1 shows such a level.
On the right side of the screen it’s the truth table you are expected to match. The input and output values might be random, depending on the level. E.g. if the inputs are narrow enough to be able to exhaust all possible input values, then the truth table is hardcoded and will never change. Otherwise, the inputs are randomized every time you start the level.
Since there’s no clock involved in these levels, the only property you might want to optimize is the total number of transistors used to create the circuit. More details on this below.
In this type of level, your goal is to build a circuit which will produce the expected output at specific simulation timesteps. Figure 2 shows such a level.
The timing graph at the bottom of the screen shows the clock, the input values at specific timesteps and the expected outputs.
Side note: There are two different timelines in DLS: the steady state timeline and the transient (or internal) timeline. Given an initial steady state of a circuit, every change to an input value generates an event at time 0 of the internal timeline. Depending on the type of gates this input is connected to, new events are generated at future internal timesteps, based on the gate’s propagation delay. This cycle of event generation and propagation is repeated until no more new events are generated. At this point the circuit has reached a new steady state and the steady state timeline is advanced. The timing graph in all sequential levels corresponds to the steady state timeline. It doesn’t matter how long it took the result to be calculated, in terms of internal timesteps/gate delays, as long as the circuit reaches a new steady state.
In contrast to the combinational levels, these levels include a clock, but since its value over time is hardcoded, the only property you might want to optimize is the total number of transistors used to create the circuit.
Stream processing circuits
The last type of level is, in my opinion, the most interesting. You get as input a stream of data and a clock. Your goal is to create a circuit which will read the input stream(s) and generate the expected output stream(s). In this case, each input stream has its own load signal and each output stream has its own write signal. Every time a rising edge is detected on one of those, a new value is loaded or written from/to the corresponding port. Figure 3 shows one such level.
You can implement those circuits in any way you want. This time, the clock is continuously ticking, but since you load and write data at your own pace, one extra property you might want to optimize for is the total number of clock ticks it takes for your circuit to process all the data. E.g. if you are able to produce 2 or more values in the same clock tick, you only have to produce 2 rising edges on the corresponding write output. The same is true for loading the input streams.
Transistor Count and Maximum Clock Frequency
I wanted to add some form of scoring mechanism to all the levels and the total number of transistors used in the circuits is the most common one I could use. The number of transistors for each build-in gate is supposed to be based on a CMOS implementation. E.g. a 2-input NAND/NOR gate requires 4 transistors, an inverter gate requires 2 transistors and a 2-input AND/OR gate requires 6 transistors (4 for the NAND/NOR + 2 for the inverter).
Another way of measuring the efficiency of a circuit is to calculate the delay of its critical path. Knowing the critical path delay, we can estimate the maximum clock frequency a circuit can operate with.
Unfortunately, calculating the critical path of an arbitrary circuit isn’t that simple. As far as I know, all the algorithms used for such a task by logic simulators use a dedicated flip flop component in order to be able to break a sequential circuit into combinational parts and sync points. Knowing that kind of info, one can calculate the critical path of each combinational part. The slowest part is the own which indicates the maximum operating clock frequency.
Since DLS doesn’t have such a dedicated flip flop component, this is nearly impossible to implement. The only way I can estimate the critical path delay of an arbitrary circuit in DLS is by repeatedly simulating the circuit with random inputs and calculating the total internal timesteps it took to produce the outputs. The problem is that in order to reliably estimate the critical path delay this way requires too many simulations, which in turn require extra time.
The advantage of this kind of measurement is that techniques such as pipelining will show their benefit by allowing faster clocks to be used. I’ll reconsider it for a future version.
That’s all for now. Hope to find some more time to work on new levels and different level types. Until then, you can send your feedback