2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,11 +1,12 @@
{{omit from|GUISS}}
[[wp:Wireworld|Wireworld]] is a cellular automaton with some similarities to [[Conway's Game of Life]].
It is capable of doing sophisticated computations with appropriate programs
(it is actually [[wp:Turing-complete|Turing complete]]),
and is much simpler to program for.
A wireworld arena consists of a cartesian grid of cells,
A Wireworld arena consists of a Cartesian grid of cells,
each of which can be in one of four states.
All cell transitions happen simultaneously.
@ -37,7 +38,9 @@ The cell transition rules are this:
| otherwise
|}
To implement this task, create a program that reads a wireworld program from a file and displays an animation of the processing. Here is a sample description file (using "<tt>H</tt>" for an electron head, "<tt>t</tt>" for a tail, "<tt>.</tt>" for a conductor and a space for empty) you may wish to test with, which demonstrates two cycle-3 generators and an inhibit gate:
;Task:
Create a program that reads a Wireworld program from a file and displays an animation of the processing. Here is a sample description file (using "<tt>H</tt>" for an electron head, "<tt>t</tt>" for a tail, "<tt>.</tt>" for a conductor and a space for empty) you may wish to test with, which demonstrates two cycle-3 generators and an inhibit gate:
<pre>
tH.........
. .
@ -46,3 +49,4 @@ tH.........
Ht.. ......
</pre>
While text-only implementations of this task are possible, mapping cells to pixels is advisable if you wish to be able to display large designs. The logic is not significantly more complex.
<br><br>

View file

@ -0,0 +1,84 @@
defmodule Wireworld do
@empty " "
@head "H"
@tail "t"
@conductor "."
@neighbours (for x<- -1..1, y <- -1..1, do: {x,y}) -- [{0,0}]
def set_up(string) do
lines = String.split(string, "\n", trim: true)
grid = Enum.with_index(lines)
|> Enum.flat_map(fn {line,i} ->
String.codepoints(line)
|> Enum.with_index
|> Enum.map(fn {char,j} -> {{i, j}, char} end)
end)
|> Enum.into(Map.new)
width = Enum.map(lines, fn line -> String.length(line) end) |> Enum.max
height = length(lines)
{grid, width, height}
end
# to string
defp to_s(grid, width, height) do
Enum.map_join(0..height-1, fn i ->
Enum.map_join(0..width-1, fn j -> Map.get(grid, {i,j}, @empty) end) <> "\n"
end)
end
# transition all cells simultaneously
defp transition(grid) do
Enum.into(grid, Map.new, fn {{x, y}, state} ->
{{x, y}, transition_cell(grid, state, x, y)}
end)
end
# how to transition a single cell
defp transition_cell(grid, current, x, y) do
case current do
@empty -> @empty
@head -> @tail
@tail -> @conductor
_ -> if neighbours_with_state(grid, x, y) in 1..2, do: @head, else: @conductor
end
end
# given a position in the grid, find the neighbour cells with a particular state
def neighbours_with_state(grid, x, y) do
Enum.count(@neighbours, fn {dx,dy} -> Map.get(grid, {x+dx, y+dy}) == @head end)
end
# run a simulation up to a limit of transitions, or until a recurring
# pattern is found
# This will print text to the console
def run(string, iterations\\25) do
{grid, width, height} = set_up(string)
Enum.reduce(0..iterations, {grid, %{}}, fn count,{grd, seen} ->
IO.puts "Generation : #{count}"
IO.puts to_s(grd, width, height)
if seen[grd] do
IO.puts "I've seen this grid before... after #{count} iterations"
exit(:normal)
else
{transition(grd), Map.put(seen, grd, count)}
end
end)
IO.puts "ran through #{iterations} iterations"
end
end
# this is the "2 Clock generators and an XOR gate" example from the wikipedia page
text = """
......tH
. ......
...Ht... .
....
. .....
....
tH...... .
. ......
...Ht...
"""
Wireworld.run(text)

View file

@ -0,0 +1,99 @@
//Create event
/*
Wireworld first declares constants and then reads a wireworld from a textfile.
In order to implement wireworld in GML a single array is used.
To make it behave properly, there need to be states that are 'in-between' two states:
0 = empty
1 = conductor from previous state
2 = electronhead from previous state
5 = electronhead that was a conductor in the previous state
3 = electrontail from previous state
4 = electrontail that was a head in the previous state
*/
empty = 0;
conduc = 1;
eHead = 2;
eTail = 3;
eHead_to_eTail = 4;
coduc_to_eHead = 5;
working = true;//not currently used, but setting it to false stops wireworld. (can be used to pause)
toroidalMode = false;
factor = 3;//this is used for the display. 3 means a single pixel is multiplied by three in size.
var tempx,tempy ,fileid, tempstring, gridid, listid, maxwidth, stringlength;
tempx = 0;
tempy = 0;
tempstring = "";
maxwidth = 0;
//the next piece of code loads the textfile containing a wireworld.
//the program will not work correctly if there is no textfile.
if file_exists("WW.txt")
{
fileid = file_text_open_read("WW.txt");
gridid = ds_grid_create(0,0);
listid = ds_list_create();
while !file_text_eof(fileid)
{
tempstring = file_text_read_string(fileid);
stringlength = string_length(tempstring);
ds_list_add(listid,stringlength);
if maxwidth < stringlength
{
ds_grid_resize(gridid,stringlength,ds_grid_height(gridid) + 1)
maxwidth = stringlength
}
else
{
ds_grid_resize(gridid,maxwidth,ds_grid_height(gridid) + 1)
}
for (i = 1; i <= stringlength; i +=1)
{
switch (string_char_at(tempstring,i))
{
case ' ': ds_grid_set(gridid,tempx,tempy,empty); break;
case '.': ds_grid_set(gridid,tempx,tempy,conduc); break;
case 'H': ds_grid_set(gridid,tempx,tempy,eHead); break;
case 't': ds_grid_set(gridid,tempx,tempy,eTail); break;
default: break;
}
tempx += 1;
}
file_text_readln(fileid);
tempy += 1;
tempx = 0;
}
file_text_close(fileid);
//fill the 'open' parts of the grid
tempy = 0;
repeat(ds_list_size(listid))
{
tempx = ds_list_find_value(listid,tempy);
repeat(maxwidth - tempx)
{
ds_grid_set(gridid,tempx,tempy,empty);
tempx += 1;
}
tempy += 1;
}
boardwidth = ds_grid_width(gridid);
boardheight = ds_grid_height(gridid);
//the contents of the grid are put in a array, because arrays are faster.
//the grid was needed because arrays cannot be resized properly.
tempx = 0;
tempy = 0;
repeat(boardheight)
{
repeat(boardwidth)
{
board[tempx,tempy] = ds_grid_get(gridid,tempx,tempy);
tempx += 1;
}
tempy += 1;
tempx = 0;
}
//the following code clears memory
ds_grid_destroy(gridid);
ds_list_destroy(listid);
}

View file

@ -0,0 +1,218 @@
//Step event
/*
This step event executes each 1/speed seconds.
It checks everything on the board using an x and a y through two repeat loops.
The variables westN,northN,eastN,southN, resemble the space left, up, right and down respectively,
seen from the current x & y.
1 -> 5 (conductor is changing to head)
2 -> 4 (head is changing to tail)
3 -> 1 (tail became conductor)
*/
var tempx,tempy,assignhold,westN,northN,eastN,southN,neighbouringHeads,T;
tempx = 0;
tempy = 0;
westN = 0;
northN = 0;
eastN = 0;
southN = 0;
neighbouringHeads = 0;
T = 0;
if working = 1
{
repeat(boardheight)
{
repeat(boardwidth)
{
switch board[tempx,tempy]
{
case empty: assignhold = empty; break;
case conduc:
neighbouringHeads = 0;
if toroidalMode = true //this is disabled, but otherwise lets wireworld behave toroidal.
{
if tempx=0
{
westN = boardwidth -1;
}
else
{
westN = tempx-1;
}
if tempy=0
{
northN = boardheight -1;
}
else
{
northN = tempy-1;
}
if tempx=boardwidth -1
{
eastN = 0;
}
else
{
eastN = tempx+1;
}
if tempy=boardheight -1
{
southN = 0;
}
else
{
southN = tempy+1;
}
T=board[westN,northN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
T=board[tempx,northN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
T=board[eastN,northN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
T=board[westN,tempy];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
T=board[eastN,tempy];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
T=board[westN,southN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
T=board[tempx,southN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
T=board[eastN,southN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
}
else//this is the default mode that works for the provided example.
{//the next code checks whether coordinates fall outside the array borders.
//and counts all the neighbouring electronheads.
if tempx=0
{
westN = -1;
}
else
{
westN = tempx - 1;
T=board[westN,tempy];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
}
if tempy=0
{
northN = -1;
}
else
{
northN = tempy - 1;
T=board[tempx,northN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
}
if tempx = boardwidth -1
{
eastN = -1;
}
else
{
eastN = tempx + 1;
T=board[eastN,tempy];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
}
if tempy = boardheight -1
{
southN = -1;
}
else
{
southN = tempy + 1;
T=board[tempx,southN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
}
if westN != -1 and northN != -1
{
T=board[westN,northN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
}
if eastN != -1 and northN != -1
{
T=board[eastN,northN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
}
if westN != -1 and southN != -1
{
T=board[westN,southN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
}
if eastN != -1 and southN != -1
{
T=board[eastN,southN];
if T=eHead or T=eHead_to_eTail
{
neighbouringHeads += 1;
}
}
}
if neighbouringHeads = 1 or neighbouringHeads = 2
{
assignhold = coduc_to_eHead;
}
else
{
assignhold = conduc;
}
break;
case eHead: assignhold = eHead_to_eTail; break;
case eTail: assignhold = conduc; break;
default: break;
}
board[tempx,tempy] = assignhold;
tempx += 1;
}
tempy += 1;
tempx = 0;
}
}

View file

@ -0,0 +1,62 @@
//Draw event
/*
This event occurs whenever the screen is refreshed.
It checks everything on the board using an x and a y through two repeat loops and draws it.
It is an important step, because all board values are changed to the normal versions:
5 -> 2 (conductor changed to head)
4 -> 3 (head changed to tail)
*/
//draw sprites and text first
//now draw wireworld
var tempx,tempy;
tempx = 0;
tempy = 0;
repeat(boardheight)
{
repeat(boardwidth)
{
switch board[tempx,tempy]
{
case empty:
//draw_point_color(tempx,tempy,c_black);
draw_set_color(c_black);
draw_rectangle(tempx*factor,tempy*factor,(tempx+1)*factor-1,(tempy+1)*factor-1,false);
break;
case conduc:
//draw_point_color(tempx,tempy,c_yellow);
draw_set_color(c_yellow);
draw_rectangle(tempx*factor,tempy*factor,(tempx+1)*factor-1,(tempy+1)*factor-1,false);
break;
case eHead:
//draw_point_color(tempx,tempy,c_red);
draw_set_color(c_blue);
draw_rectangle(tempx*factor,tempy*factor,(tempx+1)*factor-1,(tempy+1)*factor-1,false);
draw_rectangle_color(tempx*factor,tempy*factor,(tempx+1)*factor-1,(tempy+1)*factor-1,c_red,c_red,c_red,c_red,false);
break;
case eTail:
//draw_point_color(tempx,tempy,c_blue);
draw_set_color(c_red);
draw_rectangle(tempx*factor,tempy*factor,(tempx+1)*factor-1,(tempy+1)*factor-1,false);
break;
case coduc_to_eHead:
//draw_point_color(tempx,tempy,c_red);
draw_set_color(c_blue);
draw_rectangle(tempx*factor,tempy*factor,(tempx+1)*factor-1,(tempy+1)*factor-1,false);
board[tempx,tempy] = eHead;
break;
case eHead_to_eTail:
//draw_point_color(tempx,tempy,c_blue);
draw_set_color(c_red);
draw_rectangle(tempx*factor,tempy*factor,(tempx+1)*factor-1,(tempy+1)*factor-1,false);
board[tempx,tempy] = eTail;
break;
default: break;
}
tempx += 1
}
tempy += 1;
tempx = 0;
}
draw_set_color(c_black);

View file

@ -1,61 +1,60 @@
/*REXX program displays a wire world cartesian grid of four─state cells. */
signal on halt /*handle any cell growth interruptus. */
parse arg iFID . '(' generations rows cols bare head tail wire clearScreen reps
if iFID=='' then iFID='WIREWORLD.TXT' /*should default input file be used? */
blank = 'BLANK' /*the "name" for a blank. */
generations = p(generations 100 ) /*number generations allowed. */
rows = p(rows 3 ) /*the number of cell rows. */
cols = p(cols 3 ) /* " " " " cols. */
bare = pickChar(bare blank ) /*an empty cell character. */
clearScreen = p(clearScreen 0 ) /*1 means to clear the screen*/
head = pickchar(head 'H' ) /*pick the char for the head.*/
tail = pickchar(tail 't' ) /* " " " " " tail.*/
wire = pickchar(wire . ) /* " " " " " wire.*/
reps = p(reps 2 ) /*stop program if two repeats.*/
fents=max(linesize()-1,cols) /*the fence width used after displaying*/
#reps=0; $.=bare /*at start, universe is new and barren.*/
gens=abs(generations) /*use for convenience (and short name).*/
/* [↓] read the input file. */
do r=1 while lines(iFID)\==0 /*keep reading until the End─Of─File. */
q=strip(linein(iFID),'T') /*get single line from the input file. */
_=length(q) /*obtain the length of this (input) row*/
cols=max(cols,_) /*calculate the maximum number of cols.*/
do c=1 for _; $.r.c=substr(q,c,1); end /*assign the row cells.*/
/*REXX program displays a wire world Cartesian grid of four─state cells. */
signal on halt /*handle any cell growth interruptus. */
parse arg iFID . '(' generations rows cols bare head tail wire clearScreen reps
if iFID=='' then iFID="WIREWORLD.TXT" /*should default input file be used? */
blank = 'BLANK' /*the "name" for a blank. */
generations = p(generations 100 ) /*number generations that are allowed. */
rows = p(rows 3 ) /*the number of cell rows. */
cols = p(cols 3 ) /* " " " " columns. */
bare = pickChar(bare blank ) /*an empty cell character. */
clearScreen = p(clearScreen 0 ) /*1 means to clear the screen. */
head = pickChar(head 'H' ) /*pick the character for the head. */
tail = pickChar(wire . ) /* " " " " " wire. */
reps = p(reps 2 ) /*stop program if there are 2 repeats.*/
fents=max(linesize()-1,cols) /*the fence width used after displaying*/
#reps=0; $.=bare /*at start, universe is new and barren.*/
gens=abs(generations) /*use for convenience (and short name).*/
/* [↓] read the input file. */
do r=1 while lines(iFID)\==0 /*keep reading until the End─Of─File. */
q=strip(linein(iFID), 'T') /*get single line from the input file. */
_=length(q) /*obtain the length of this (input) row*/
cols=max(cols, _) /*calculate the maximum number of cols.*/
do c=1 for _; $.r.c=substr(q, c, 1); end /*assign the row cells.*/
end /*r*/
rows=r-1 /*adjust the row number (from DO loop).*/
life=0; !.=0; call showCells /*display initial state of the cells. */
/*watch cells evolve, 4 possible states*/
do life=1 for gens; @.=bare /*perform for the number of generations*/
rows=r-1 /*adjust the row number (from DO loop).*/
life=0; !.=0; call showCells /*display initial state of the cells. */
/*watch cells evolve, 4 possible states*/
do life=1 for gens; @.=bare /*perform for the number of generations*/
do r=1 for rows /*process each of the rows.*/
do c=1 for cols; ?=$.r.c; ??=? /* " " " " cols.*/
select /*determine type of cell. */
do r=1 for rows /*process each of the rows. */
do c=1 for cols; ?=$.r.c; ??=? /* " " " " columns. */
select /*determine the type of cell. */
when ?==head then ??=tail
when ?==tail then ??=wire
when ?==wire then do; n=hood(); if n==1|n==2 then ??=head; end
when ?==wire then do; n=hood(); if n==1 | n==2 then ??=head; end
otherwise nop
end /*select*/
@.r.c=??
end /*c*/
end /*r*/
call assign$ /*assign alternate cells ──► real world*/
call assign$ /*assign alternate cells ──► real world*/
if generations>0 | life==gens then call showCells
end /*life*/
/*stop watching the universe (or life).*/
halt: if life-1\==gens then say 'The ~~~Wireworld~~~ program was interrupted.'
done: exit /*stick a fork in it, we are all done.*/
/*────────────────────────────────────────────────────────────────────────────*/
showCells: if clearScreen then 'CLS' /*◄──change this for the OS.*/
call showRows /*show rows in proper order.*/
say right(copies('',fents)life,fents) /*display a bunch of cells. */
if _=='' then signal done /*No life? Then stop run. */
if !._ then #reps=#reps+1 /*detected repeated pattern.*/
!._=1 /*it is now existence state.*/
if reps\==0 & #reps<=reps then return /*so far, so good, no reps. */
/*stop watching the universe (or life).*/
halt: if life-1\==gens then say 'The ~~~Wireworld~~~ program was interrupted.'
done: exit /*stick a fork in it, we are all done.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
showCells: if clearScreen then 'CLS' /*◄──change this for the OS.*/
call showRows /*show rows in proper order.*/
say right(copies('',fents)life,fents) /*display a bunch of cells. */
if _=='' then signal done /*No life? Then stop run. */
if !._ then #reps=#reps+1 /*detected repeated pattern.*/
!._=1 /*it is now existence state.*/
if reps\==0 & #reps<=reps then return /*so far, so good, no reps. */
say '"Wireworld" repeated itself' reps "times, program is stopping."
signal done /*exit program, we're done. */
/*───────────────────────────────one─liner subroutines─────────────────────────────────────────────────────────────────────*/
signal done /*exit program, we're done. */
/*─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────*/
$: parse arg _row,_col; return ($._row._col==head)
assign$: do r=1 for rows; do c=1 for cols; $.r.c=@.r.c; end; end; return
err: say; say center(' error! ',max(40,linesize()%2),"*"); say; do j=1 for arg(); say arg(j); say; end; say; exit 13

View file

@ -0,0 +1,46 @@
(* Maximilian Wuttke 12.04.2016 *)
type world = char vector vector
fun getstate (w:world, (x, y)) = (Vector.sub (Vector.sub (w, y), x)) handle Subscript => #" "
fun conductor (w:world, (x, y)) =
let
val s = [getstate (w, (x-1, y-1)) = #"H", getstate (w, (x-1, y)) = #"H", getstate (w, (x-1, y+1)) = #"H",
getstate (w, (x, y-1)) = #"H", getstate (w, (x, y+1)) = #"H",
getstate (w, (x+1, y-1)) = #"H", getstate (w, (x+1, y)) = #"H", getstate (w, (x+1, y+1)) = #"H"]
(* Count `true` in s *)
val count = List.length (List.filter (fn x => x=true) s)
in
if count = 1 orelse count = 2 then #"H" else #"."
end
fun translate (w:world, (x, y)) =
case getstate (w, (x, y)) of
#" " => #" "
| #"H" => #"t"
| #"t" => #"."
| #"." => conductor (w, (x, y))
| s => s
fun next_world (w : world) = Vector.mapi (fn (y, row) => Vector.mapi (fn (x, _) => translate (w, (x, y))) row) w
(* Test *)
(* makes a list of strings into a world *)
fun make_world (rows : string list) : world =
Vector.fromList (map (fn (row : string) => Vector.fromList (explode row)) rows)
(* word_str reverses make_world *)
fun vec_str (r:char vector) = implode (List.tabulate (Vector.length r, fn x => Vector.sub (r, x)))
fun world_str (w:world) = List.tabulate (Vector.length w, fn y => vec_str (Vector.sub (w, y)))
fun print_world (w:world) = (map (fn row_str => print (row_str ^ "\n")) (world_str w); ())
val test = make_world [
"tH.........",
". . ",
" ... ",
". . ",
"Ht.. ......"]