#!/home/arne/wisp/wisp-multiline.sh 
; !#

; A world projected on a d20 (20-sided die, ikosaeder)

; For this we need a vector with 20 elements, a vector which shows the
; neighboring elements and accessor functions which give us the
; relevant elements for any set of longitude and latitude as well as
; its inverse (element-id to lon+lat). For further subdivisions, just
; elevate the center of each edge and connect these centers.

define-module : examples d20world
              . #:export : world neighbors d20-as-text d20-diffuse

define world : make-vector 20 0
define neighbors : make-vector 20
; count from the top

; Contains the numbers instead of the indexes, to make it easier for
; me to think about them.
; 
;   7       8
;    3    4  
;       1        
;   6   2   9
;     5   10
; 
;    14       13
;     18    17
;        20        
;   15   19   12
;     16    11
; 
define neighbors-helper
  ' : 1 2 3 4
      2 1 5 10
      3 1 6 7
      4 1 8 9
      5 2 6 14
      6 3 5 15
      7 3 8 16
      8 4 7 11
      9 4 10 12
      10 1 9 13
      20 19 18 17
      19 20 16 11
      18 20 15 14
      17 20 13 12
      16 19 17 7
      15 18 16 6
      14 18 13 5
      13 17 14 10
      12 17 11 9
      11 19 12 8

let loop : : relationships neighbors-helper
  cond 
   : null? relationships
     . neighbors
   else
    let* 
      : cur : car relationships
          idx : 1- : car cur
          vec : cdr cur
      vector-set! world idx : 1+ idx
      vector-set! neighbors idx : make-vector 3
      let setidx : : idxtoset '(0 1 2)
        cond 
         : null? idxtoset
           ; the outer loop continues here
           loop : cdr relationships
         else
            vector-set!
                vector-ref neighbors idx
                car idxtoset
                1- : list-ref vec : car idxtoset
            setidx : cdr idxtoset

define : d20-as-text world-vector
         . "show the given d20 world as text"
         let 
           : template "
 ~A         ~A
   ~A    ~A  
      ~A        
 ~A    ~A    ~A
    ~A   ~A

 ~A       ~A
  ~A    ~A
     ~A        
~A   ~A   ~A
  ~A    ~A
"
             indexes ' : 7 8 3 4 1 6 2 9 5 10 14 13 18 17 20 15 19 12 16 11
           apply format : append (list #f template) : map (lambda (x) (vector-ref world (1- x))) indexes

define : d20-diffuse world neighbors D
         . "Diffuse the values on the d20 using the diffusion constant D. Step 1: Simply iterative (=wrong)."
         let loop : : neighbors-to-diffuse : iota : vector-length neighbors
             cond 
               : null? neighbors-to-diffuse
                 . world
               else
                 let : : edges-to-diffuse-targets : vector-ref neighbors (car neighbors-to-diffuse)
                         let*
                           : edges-to-diffuse : append (list (car neighbors-to-diffuse)) : vector->list edges-to-diffuse-targets
                             idx0 : list-ref edges-to-diffuse 0
                             val0 : vector-ref world idx0
                             idx1 : list-ref edges-to-diffuse 1
                             val1 : vector-ref world idx1
                             idx2 : list-ref edges-to-diffuse 2
                             val2 : vector-ref world idx2
                             idx3 : list-ref edges-to-diffuse 3
                             val3 : vector-ref world idx3
                           vector-set! world idx0 : + val0 : * D : - val1 val0
                           vector-set! world idx1 : - val1 : * D : - val1 val0
                           vector-set! world idx0 : + val0 : * D : - val2 val0
                           vector-set! world idx2 : - val2 : * D : - val2 val0
                           vector-set! world idx0 : + val0 : * D : - val3 val0
                           vector-set! world idx3 : - val3 : * D : - val3 val0
                 loop : cdr neighbors-to-diffuse


display world
newline
display neighbors
newline
display : vector-ref world 0
newline
display : d20-as-text world
newline
d20-diffuse world neighbors 0.01
display : d20-as-text world
newline