( exports: level )

constant NOWAY

: 20array 
    create 20 cells allot
    does> swap + ;
20array (con-edges)
variable n-edge 

variable start-room 
variable exit-room
variable farthest

: init-tree ( -- )
    10 1 do
        i room-cut! loop ;

: init-edges ( -- )
    n-edge off
    20 0 do 0 i (con-edges) ! loop ;

: edge+! ( edge -- )
    4 lshift or n-edge dup -rot @ (con-edges) c! 1 swap +! ;

: edge@ ( en -- edge )
    (con-edges) c@ dup 15 and swap 4 rshift ;

\ remove last edge
: edge-! ( -- ) 
    n-edge dup @ if -1 swap +! then ;

: dump-sets
    cr
    10 1 do
        i room-trunk? if [CHAR] T emit 
        else i room-thru? if [CHAR] + emit
        else i room-exists? if [CHAR] 0 emit
        else [CHAR] _ emit then then then
        1 spaces
        i 1- 3 mod 2 = if cr then
    loop 

    n-edge @ if
    n-edge @ 0 do
        i edge@ [CHAR] ( emit . . [CHAR] ) emit
    loop cr then ;

\ return true if the room is nonexistent or was visited before
: cannot-go? ( i -- b )
    dup room-exists? not swap room-trunk? or ;

\ Find where we can go starting from given room number.
\ Possible directions are defined as a table of hex numbers.
\ The number of results is variable 0..4, 
\ they are room numbers 1..9.
( rn -- directions )
: make-could-go
    create , , , , , , , , , ,
    does> swap cells + @ 
        begin
            dup 15 and dup cannot-go? if drop else swap then
            4 rshift dup 0= until drop ;            

hex 68 579 48 359 2468 157 26 135 24 0 decimal
make-could-go where? 

\ pick start room and make it trunk
: pick-start ( -- i ) 
    0 ( put a dummy to drop )
    100 0 do
        9 rnd1+ 
        dup room-exists? over room-thru? not and
        if nip leave then
        drop
    loop ;

\ pick a random room to go to
: go-to ( from -- to )
    depth >r where? depth r@ - 1+ 
        dup 0> if rnd pick else NOWAY then
        depth r> - 0 do nip loop ;

\ prune the edge that connects a dead-end thru room
: prune-last ( rn -- )
    n-edge @ if 
        dup                         ( rn rn -- )
        n-edge @ 1- edge@           ( rn rn a b -- )
        drop = if 
            -1 n-edge +!
            room-nexisteplus!
        else
            drop
        then 
    then ;

: farthest?! ( rn -- )
    tsdepth farthest @ > if
        tsdepth farthest !
        exit-room !
        exit 
    then
    drop ;

: push&go ( cur next -- )
    dup rot dup 
    >tstack edge+!
    dup room-trunk!
    dup room-thru? not if
        dup farthest?!
    then ;

: back&track ( cur next -- )
    drop dup                ( cur next -- cur cur )
    room-thru? if dup prune-last then drop
    tstack> ;    ( -- last-position )

\ pick a random room and walk the maze, create edges as we go
: build-tree
    0 farthest !
    pick-start 
    dup 0= if drop exit then        \ could not pick
    dup room-trunk! dup start-room ! dup exit-room !
    begin
        ( rn -- )
        dup go-to dup if 
            push&go
        else
            back&track ?dup not if exit then
        then
    again ;

\ return true if the rooms are all connected
: tree-complete?
    10 1 do
        i room-exists? if
            i room-trunk? not if
                R> R> 2drop ( unloop )
                false exit
            then
        then
    loop
    true ;

: render-passages
    n-edge @ if
        n-edge @ 0 do
            i edge@ connect-2rooms
        loop then ;

: add-some-thru
    10 1 do i room-exists? not if 
        coinflip if i room-thru then 
    then loop ;

: linked-rooms
    NOTHING dclear
    make-rooms
    begin 
        init-tree 
        init-edges 
        build-tree 
        tree-complete? if exit then 
        add-some-thru
    again ;

: place-thing ( rn cls -- )
    swap somewhere-in-room ( cls x y )
    3dup thing-new }t-class c! 
    rot drop dset-thing ;

: place-monster ( rn monster -- )
    2dup
        swap somewhere-in-room ( cls x y )
        3dup rot C-MONSTER + -rot 
        thing-new dup >r }t-class c!
        rot drop dset-monster 
    drop r> }t-room c! ;

: add-junk ( rn -- )
    dup rnd-static-thing place-thing 
    coinflip if 
        rnd-monster-class place-monster
    else
        drop
    then ;

: foreach-room
    10 1 do
        i room-exists? if 
            i room-thru? not if
                dup i swap execute 
        then then
    loop drop ;

: place-things ( -- )
    things-clear
    ['] add-junk foreach-room ;

: level
    linked-rooms render-passages render-rooms 
    place-things
    exit-room @ C-EXIT place-thing ;