filler.ob07 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. MODULE filler; (* filler game, color more fields than the opponent *)
  2. IMPORT
  3. SYSTEM,
  4. out IN "./common/out.ob07",
  5. unix IN "./common/unix.ob07",
  6. gr IN "./common/gr.ob07";
  7. CONST
  8. Side = 11; (* nr of pixels of a field side *)
  9. width = 62; height = 48; (* board size *)
  10. nrFields = width * height;
  11. BackGroundColor = 0B0B050H;
  12. VAR fdRandom :INTEGER; (* /dev/urandom *)
  13. base, stride, screenBufSize :INTEGER;
  14. palette :ARRAY 6 OF INTEGER;
  15. field :ARRAY nrFields OF INTEGER; (* color 0..5 *)
  16. visit :ARRAY nrFields OF INTEGER; (* 0 unvisited, 1 neighbour to do, 2 done *)
  17. Acount, Acolor, Bcount, Bcolor :INTEGER; (* player conquered fields and current color *)
  18. rndSeed, rndIndex :INTEGER;
  19. PROCEDURE check (b :BOOLEAN; n :INTEGER);
  20. BEGIN
  21. IF ~b THEN
  22. out.formatInt ("internal check failed: filler.mod: %", n); out.nl;
  23. out.exit(1)
  24. END
  25. END check;
  26. PROCEDURE random6 () :INTEGER; (* return random 0..5 *)
  27. VAR n :INTEGER;
  28. b :BYTE;
  29. BEGIN
  30. IF rndIndex = 3 THEN
  31. (* 6 ^ 3 = 216 so 3 random6 nrs fit in one random byte, don't waste entropy *)
  32. n := unix.readByte (fdRandom, b); ASSERT (n = 1);
  33. rndSeed := b; rndIndex := 0;
  34. END;
  35. n := rndSeed MOD 6; rndSeed := rndSeed DIV 6; INC (rndIndex)
  36. RETURN n
  37. END random6;
  38. PROCEDURE drawRect (x, y, color :INTEGER);
  39. VAR p, i, j :INTEGER;
  40. BEGIN
  41. p := (y*stride + x*4)*Side;
  42. check (p + (Side-1)*stride + (Side-1)*4 <= screenBufSize, 20);
  43. p := base + p;
  44. FOR j := 0 TO Side-1 DO
  45. FOR i := 0 TO Side-1 DO SYSTEM.PUT32 (p, color); INC(p, 4) END;
  46. p := p + stride - Side*4;
  47. END;
  48. END drawRect;
  49. PROCEDURE clearVisit;
  50. VAR i :INTEGER;
  51. BEGIN FOR i := 0 TO nrFields-1 DO visit[i] := 0 END; END clearVisit;
  52. PROCEDURE doNeighbour (i, old, new, v :INTEGER; VAR changed :BOOLEAN);
  53. (* helper routine for connect() *)
  54. BEGIN
  55. IF visit[i] = 0 THEN
  56. IF (v = 1) & (field[i] = old) THEN visit[i] := 1; changed := TRUE END;
  57. IF field[i] = new THEN visit[i] := 2; changed := TRUE END
  58. END
  59. END doNeighbour;
  60. (*
  61. all visit := 0; count := 0; visit[corner] := 1
  62. repeat
  63. changed := false;
  64. foreach:
  65. if (visit = 1) or (visit = 2) then
  66. curVisit = visit
  67. color := new; visit := 3; count++
  68. foreach neighbour:
  69. if visit = 0 then
  70. if curVisit = 1 then
  71. if color = old then visit := 1; changed := true
  72. if color = new then visit := 2; changed := true
  73. if curVisit = 2 then
  74. if color = new then visit := 2; changed := true
  75. until no changes
  76. *)
  77. PROCEDURE connect (old, new :INTEGER) :INTEGER;
  78. VAR count, i, x, y, v :INTEGER;
  79. changed :BOOLEAN;
  80. BEGIN
  81. out.formatInt2 ("connect: old new % % ", old+1, new+1);
  82. count := 0;
  83. REPEAT
  84. changed := FALSE;
  85. FOR i := 0 TO nrFields-1 DO
  86. v := visit[i];
  87. IF (v=1) OR (v=2) THEN
  88. field[i] := new; visit[i] := 3; INC(count);
  89. x := i MOD width; y := i DIV width;
  90. IF x > 0 THEN doNeighbour (i-1, old, new, v, changed) END;
  91. IF x < width-1 THEN doNeighbour (i+1, old, new, v, changed) END;
  92. IF y > 0 THEN doNeighbour (i-width, old, new, v, changed) END;
  93. IF y < height-1 THEN doNeighbour (i+width, old, new, v, changed) END;
  94. END
  95. END
  96. UNTIL ~changed
  97. RETURN count
  98. END connect;
  99. PROCEDURE doMaxGainNeighbour (i, old, new, v :INTEGER; VAR changed :BOOLEAN);
  100. (* helper routine for maxGain() *)
  101. BEGIN
  102. IF visit[i] = 0 THEN
  103. IF v = 1 THEN
  104. IF field[i] = old THEN visit[i] := 1 ELSE visit[i] := 2 END;
  105. changed := TRUE
  106. ELSE
  107. IF field[i] = new THEN visit[i] := 2; changed := TRUE END
  108. END
  109. END
  110. END doMaxGainNeighbour;
  111. (* v=1 & field=old -> visit := 1
  112. v=1 & field # old -> visit := 2
  113. v=2 & field = new -> visit := 2
  114. *)
  115. PROCEDURE maxGain (old :INTEGER) :INTEGER;
  116. (* return the color which will conquer the most fields *)
  117. VAR
  118. i, x, y, new, v :INTEGER;
  119. max :ARRAY 6 OF INTEGER;
  120. changed :BOOLEAN;
  121. BEGIN
  122. FOR i := 0 TO 5 DO max[i] := 0 END;
  123. REPEAT
  124. changed := FALSE;
  125. FOR i := 0 TO nrFields-1 DO
  126. v := visit[i];
  127. IF (v=1) OR (v=2) THEN
  128. visit[i] := 3; new := field[i]; INC (max[new]);
  129. x := i MOD width; y := i DIV width;
  130. IF x > 0 THEN doMaxGainNeighbour (i-1, old, new, v, changed) END;
  131. IF x < width-1 THEN doMaxGainNeighbour (i+1, old, new, v, changed) END;
  132. IF y > 0 THEN doMaxGainNeighbour (i-width, old, new, v, changed) END;
  133. IF y < height-1 THEN doMaxGainNeighbour (i+width, old, new, v, changed) END;
  134. END
  135. END
  136. UNTIL ~changed;
  137. x := -1; y := -1; max[Acolor] := -1; max[Bcolor] := -1;
  138. out.str ("maxGain"); out.nl;
  139. FOR i := 0 TO 5 DO out.formatInt2 (" % %", i+1, max[i]); out.nl END;
  140. FOR i := 0 TO 5 DO IF (max[i] > y) & (i # old) THEN x := i; y := max[i] END END
  141. RETURN x
  142. END maxGain;
  143. PROCEDURE drawAll;
  144. VAR x, y :INTEGER;
  145. BEGIN
  146. gr.screenBegin;
  147. gr.clear (BackGroundColor);
  148. FOR y := 0 TO 5 DO drawRect (0, 6 + y DIV 3 + 2*y, palette[y]) END;
  149. FOR y := 0 TO 47 DO
  150. FOR x := 0 TO 61 DO drawRect (x+2, y, palette[ field[y*width + x] ]) END
  151. END;
  152. gr.screenEnd;
  153. END drawAll;
  154. PROCEDURE run*;
  155. VAR stop :BOOLEAN;
  156. ev :gr.EventPars;
  157. x, y, i, old :INTEGER;
  158. ch :CHAR;
  159. BEGIN
  160. FOR i := 0 TO nrFields-1 DO field[i] := random6() END;
  161. Acolor := field[47*width]; field[47*width+1] := Acolor; field[46*width] := Acolor; field[46*width+1] := Acolor;
  162. Bcolor := field[width-1]; field[width-2] := Bcolor; field[2*width-2] := Bcolor; field[2*width-1] := Bcolor;
  163. base := gr.base; stride := gr.stride;
  164. gr.createWindow (800, 600);
  165. screenBufSize := gr.winHeight * stride;
  166. stop := FALSE;
  167. drawAll;
  168. REPEAT
  169. gr.nextEvent (0, ev);
  170. IF ev[0] = gr.EventKeyPressed THEN
  171. (* o.formatInt("key pressed %",ev[2]);o.nl; *)
  172. (* ev[2]: q=24, ESC=9, CR=36 *)
  173. ch := CHR (ev[4]);
  174. IF ev[2] = 9 THEN stop := TRUE END; (* ESC *)
  175. (* IF ch = "q" THEN stop := TRUE END; *)
  176. IF (ch >= "1") & (ch <= "6") THEN
  177. i := ev[4] - ORD("1");
  178. IF (i # Acolor) & (i # Bcolor) THEN
  179. (* player A *)
  180. old := Acolor; Acolor := i;
  181. out.formatInt ("play color %", Acolor+1); out.nl;
  182. clearVisit; visit[47*width] := 1;
  183. Acount := connect (old, Acolor)
  184. ;out.formatInt ("count A = %", Acount); out.nl; out.nl;
  185. (* player B *)
  186. clearVisit; visit[width-1] := 1; old := field[width-1];
  187. Bcolor := maxGain (old);
  188. clearVisit; visit[width-1] := 1;
  189. Bcount := connect (old, Bcolor);
  190. out.formatInt ("count B = %", Bcount); out.nl; out.nl;
  191. drawAll;
  192. END
  193. END;
  194. ELSIF ev[0] = gr.EventButtonPressed THEN
  195. x := ev[2] DIV Side; y := ev[3] DIV Side;
  196. END;
  197. UNTIL stop;
  198. gr.finish;
  199. unix.finish;
  200. END run;
  201. BEGIN
  202. fdRandom := unix.open ("/dev/urandom", unix.O_RDONLY, 0); ASSERT (fdRandom # -1);
  203. rndIndex := 3;
  204. (* a partial copy of the lexaloffle pico-8 16-color palette *)
  205. palette[0] := 0FF004DH; (* red *)
  206. palette[1] := 0FFA300H; (* orange *)
  207. palette[2] := 07E2553H; (* dark purple *)
  208. palette[3] := 0008751H; (* dark green *)
  209. palette[4] := 029ADFFH; (* blue *)
  210. palette[5] := 0FF77A8H; (* pink *)
  211. run;
  212. END filler.