Skip to content

Instantly share code, notes, and snippets.

@emlun
Last active September 9, 2017 19:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emlun/d375b3b843499fb7d3b2ae41d9e8442a to your computer and use it in GitHub Desktop.
Save emlun/d375b3b843499fb7d3b2ae41d9e8442a to your computer and use it in GitHub Desktop.
Shenzhen I/O: Frobnicator
-- The function get_name() should return a single string that is the name of the puzzle.
--
function get_name()
return "Frobnicator"
end
-- The function get_description() should return an array of strings, where each string is
-- a line of description for the puzzle. Surrounding text with asterisks will cause it to
-- be rendered in bold, something we use when mentioning a signal by name.
--
-- By using the syntax that we use in our puzzle descriptions, you can also create tables:
--
-- "| This is a table. | This is a second column. | This is a column with "
-- "| | | multiple line of text."
-- "----------------------------------------------------------------------"
-- "| This is a second | | More text here! "
-- "| table row. | | "
--
-- Puzzle descriptions are not automatically paginated, so you can use the string "<PAGE>"
-- to start a new page of text. If you don't it will overflow and make Zach sad.
--
function get_description()
return {
"*prog* is a simple input connected to a push button.",
"*keypad* is a non-blocking XBus input connected to a numeric keypad.",
"*data-in* is a non-blocking XBus input connected to an electronic device generating nonnegative numbers.",
"*data-out* is an XBus output connected to an electronic device.",
"<PAGE>",
"When data arrives on *data-in*, frobnicate it as described below and write the result to *data-out*.",
"While *prog* is pressed, read decimal digits from *keypad* until *prog* is released. Create a number _k_ from the up to 3 last digits, with the last digit to arrive as the least significant digit. If _k <_ 500, set the frobnication key _K_ to _k_. If _k >=_ 500, do not change _K_. _K_ is initially zero.",
"A key _K_ frobnicates a number _n_ to _f(n, K)_, where:\nIf _n = K_, then _f(n, K) = n_. If _n < K_, then _f(n, K) = K + (K - n)_. If _n > K_ then _f(n, K) = K - (n - K)_.",
}
end
-- The function get_board() allows you to specify an 18x7 "ASCII art" grid to customize
-- the layout of the board and the placement of the input and output terminals. Generally
-- speaking, inputs are placed on the left of boards, while outputs are placed on the right.
--
-- For empty space, use the '.' character.
-- For buildable board space, use the '#' character.
-- For an input or output terminal, use characters '0' through '9' no more than once each.
-- For the bottom-left corner of the radio, use the 'R' character.
-- For the bottom-left corners of dials, use characters 'A', 'B', and 'C'.
--
function get_board()
return [[
.0###############.
.################.
.################.
.1##############3.
.################.
.################.
.2###############.
]]
end
-- The function get_data() is called both to establish information about the puzzle (such as what
-- the inputs and outputs are) and to generate the random test cases. Signal levels and XBus data
-- should change from call to call, but information like the names and types of terminals should
-- not change at all.
--
-- To create a standard input or output terminal, call create_terminal(). Valid terminal types are
-- TYPE_SIMPLE, TYPE_XBUS, and TYPE_XBUS_NONBLOCKING. Valid terminal directions are DIR_INPUT and
-- DIR_OUTPUT. Valid data for a simple I/O signal is an array of integers, 0 - 100 inclusive. Valid
-- data for an XBus signal is an array of integer arrays, each with values -999 to 999 inclusive.
--
-- create_terminal(name, board_character, type, direction, data)
--
-- To create a radio (C2S-RF901), call create_radio(). You may only create one radio in each puzzle.
-- Since radios are XBus-only, the only valid data for data_rx and data_tx are arrays of integer arrays,
-- each with values -999 to 999 inclusive. You cannot customize the signal names for a radio.
--
-- By default the radio will be placed in the bottom-left corner of the screen. However, if you use an
-- 'R' character in your board layout, the bottom-left corner of the radio will be placed there instead.
--
-- create_radio(data_rx, data_tx)
--
-- To create a dial (N4DL-1000), call create_dial(). You may create up to three dials in each puzzle.
-- The names of dials should be kept short, as there is not much room to display them visually. A valid
-- value is an integer between 0 and 99, inclusive.
--
-- By default dials will be placed in the bottom-left corner of the screen. However, if you use an
-- 'A', 'B', or 'C' character in your board layout, the bottom-left corners of the first, second, and
-- third dials will be placed there, respectively.
--
-- create_dial(name, value)
--
-- NOTE: To generate random values you should use math.random(). However, you SHOULD NOT seed
-- the random number generator with a new seed value, as that is how the game ensures that
-- the first test run is consistent for all users, and thus something that allows for the
-- comparison of cycle scores.
--
-- NOTE: Fun fact! Arrays in Lua are implemented as tables (dictionaries) with integer keys that
-- start at 1 by convention. Contrast this with nearly every other programming language, in
-- which arrays start with an index of 0. Because of this, the 60 "time slices" that make
-- up a test case are indexed from 1 to 60 inclusive.
--
function frob(n, k)
if n == k then
return n
elseif n < k then
return (k + (k - n))
else
return (k - (n - k))
end
end
function get_data()
keypad = {}
prog = {}
data_in = {}
data_out = {}
T = 60
frobkey = {}
for t = 1, T do
frobkey[t] = 0
end
time = 1
while time <= T do
time = time + math.random(5,10)
progtime = math.random(3, math.min(8, T - 5 - time))
for j = 0, progtime - 1 do
prog[time + j] = 100
end
key_input = {}
key_idx = 1
keytime = -2
keytime = keytime + math.random(1, 4)
while keytime <= progtime + 2 do
input = math.random(1, 9)
keypad[time + keytime] = { input }
if keytime >= 0 and keytime < progtime then
key_input[key_idx] = input
key_idx = key_idx + 1
end
keytime = keytime + math.random(1, 4)
end
key = 0
if key_idx > 3 then
key = key + 100 * key_input[key_idx - 3]
end
if key_idx > 2 then
key = key + 10 * key_input[key_idx - 2]
end
if key_idx > 1 then
key = key + key_input[key_idx - 1]
end
if key < 500 and progtime >= 1 then
for t = time + progtime, T do
frobkey[t] = key
end
end
time = time + progtime + 1
end
time = 1
while time <= T do
if math.random(1, 3) == 1 then
num_packets = math.random(1, math.min(5, 59 - time))
data_in[time] = {}
data_out[time] = {}
for i = 1, num_packets do
data_in[time][i] = math.random(0, 999)
data_out[time][i] = frob(data_in[time][i], frobkey[time])
end
time = time + num_packets
else
time = time + 1
end
end
create_terminal("keypad", "0", TYPE_XBUS_NONBLOCKING, DIR_INPUT, keypad)
create_terminal("prog", "1", TYPE_SIMPLE, DIR_INPUT, prog)
create_terminal("data-in", "2", TYPE_XBUS_NONBLOCKING, DIR_INPUT, data_in)
create_terminal("data-out", "3", TYPE_XBUS, DIR_OUTPUT, data_out)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment