Black box
Implement a version of the Black Box game beginners configuration: 4 Atoms in an 8 x 8 grid.
Determine where the hidden atoms are in the box, by observing how the light beams fired into the box react when leaving it.
Possible results:
'H': the beam hit an atom and stopped
'R': Either the beam was reflected back the way it came or there was a ball just to one side of its entry point
'Numbers': indicate that the beam entered one of those squares and emerged from the other
Extra credit (Different game types):
-More or less atoms (maybe random)
-Different grid sizes
AutoHotkey
SetBatchLines -1
;--------------------------------------------------------------------------------------
BoardSize := 8
GUI()
setupBoard()
OnMessage(0x0201, "WM_LBUTTONDOWN")
return
;--------------------------------------------------------------------------------------
GUI(){
global
BoardSize += 2 ; add left/right and top/buttom
lastIndex := BoardSize-1 ; 0-based
symbol := {}, w := h := 30
Menu, FileMenu, Add, Manual Entry, MenuHandler
Menu, FileMenu, Add, E&xit, MenuHandler
Menu, MyMenuBar, Add, &File, :FileMenu
Gui, Menu, MyMenuBar
Gui, font, s14, Consolas
loop % BoardSize**2
{
r := (A_Index-1)//BoardSize, c := Mod(A_Index-1, BoardSize)
options := r = 0 ? " v" r "_" c " gSendRay"
: c = 0 ? " v" r "_" c " gSendRay"
: c = lastIndex ? " v" r "_" c " gSendRay"
: r = lastIndex ? " v" r "_" c " gSendRay"
: " v" r "_" c
if (c = 0 && r = 0)
Gui, add, button, % "section x14 y14 w" w " h" h options
else if c = 0
Gui, add, button, % "section x14 y+0 w" w " h" h options
else
Gui, add, button, % "x+0 w" w " h" h options
}
for i, v in StrSplit("0_0,0_" lastIndex "," lastIndex "_0," lastIndex "_" lastIndex "", ",")
GuiControl, hide, % v
Gui, font, s10, Consolas
Gui, add, button, xs w80 vButtonDone gDone Disabled, % ButtonDoneText := "Done"
Gui, add, text, x+10, % "?? = Hit, ? = Reflection"
Gui, add, text, y+5 , % "Atoms Found = "
Gui, add, text, x+0 vTextAtom w80
Gui, +AlwaysOnTop
Gui, show,, Black Box
}
;--------------------------------------------------------------------------------------
GuiClose:
ExitApp
return
;--------------------------------------------------------------------------------------
MenuHandler(){
global
if (A_ThisMenuItem = "Manual Entry")
{
Menu, FileMenu, ToggleCheck, Manual Entry
if (Manual_Entry := !Manual_Entry)
resetBoard()
else
Board := [], mapBoard()
}
if (A_ThisMenuItem = "E&xit")
ExitApp
}
;--------------------------------------------------------------------------------------
setupBoard(){ ; land mines in random spots on PlayField
global
resetBoard()
if Manual_Entry
return
Random, atoms, % Floor(BoardSize/2)-1, % Floor(BoardSize/2)
;~ atoms += 8
loop % atoms
{
Random, rnd, 1, PlayField.Count()
x := PlayField.RemoveAt(rnd)
Mines[x.1, x.2] := true
}
mapBoard()
}
;--------------------------------------------------------------------------------------
resetBoard(){ ; Reset All
global
Board:=[], PlayField:=[], Mines:=[], Solution:=[], symbol:=[], found:=atoms:=0
loop % BoardSize*4
symbol.Push(Chr(0x0387+A_Index))
loop % BoardSize**2
{
r := (A_Index-1)//BoardSize, c := Mod(A_Index-1, BoardSize)
if (r>0 && r<lastIndex && c>0 && c<lastIndex)
PlayField.Push([r , c])
}
mapBoard()
}
;--------------------------------------------------------------------------------------
mapBoard(){ ; map all buttons to reflect Board
global
loop % BoardSize**2
{
r := (A_Index-1)//BoardSize, c := Mod(A_Index-1, BoardSize)
GuiControl,, % r "_" c, % ""
GuiControl,, % r "_" c, % v := Board[r, c]
if (r>0 && r<lastIndex && c>0 && c<lastIndex)
GuiControl, % (v = "" || v = "?" || v = "+") ? "Disable" : "Enable", % r "_" c
}
GuiControl,, ButtonDone, % ButtonDoneText
GuiControl,, TextAtom, % found " / " atoms
GuiControl, % found = atoms ? "Enable" : "Disable", ButtonDone
}
;--------------------------------------------------------------------------------------
WM_LBUTTONDOWN(){
global
MouseGetPos, mx, my, mw, buttonNum
buttonNum := StrReplace(buttonNum, "Button") - 1
r := buttonNum//BoardSize, c := Mod(buttonNum, BoardSize)
if !(R>0 && r<lastIndex && c>0 && c<lastIndex)
return
if Manual_Entry
{
Mines[r, c] := !Mines[r, c]
Board[r, c] := Mines[r, c] ? "?" : ""
atoms := Mines[r, c] ? atoms+1 : atoms-1
}
else
{
Solution[r, c] := !Solution[r, c]
Board[r, c] := Solution[r, c] ? "??" : ""
found := Board[r, c] ? found + 1 : found -1
}
mapBoard()
}
;--------------------------------------------------------------------------------------
Done(){
global
if (ButtonDoneText = "done")
{
ButtonDoneText := ":)"
for r, obj in Solution
for c, bool in obj
if Solution[r, c] && (Mines[r, c] = Solution[r, c])
Board[r, c] := "?" ; right
else if Solution[r, c] && (Mines[r, c] <> Solution[r, c])
Board[r, c] := "?" , ButtonDoneText := ":(" ; wrong marking
for r, obj in Mines
for c, bool in obj
if Mines[r, c] && (Mines[r, c] <> Solution[r, c])
Board[r, c] := "?" , ButtonDoneText := ":(" ; missed marking
mapBoard()
}
else
{
ButtonDoneText := "Done"
setupBoard()
}
}
;--------------------------------------------------------------------------------------
SendRay(){
global
; troubleshooting
if TroubleShooting
{
loop % BoardSize**2
r := (A_Index-1)//BoardSize, c := Mod(A_Index-1, BoardSize)
, Board[r, c] := Board[r, c] = "+" ? "" : Board[r, c]
mapBoard()
}
x := StrSplit(A_GuiControl, "_")
r := x.1, c := x.2
dir := (r = 0) ? "D" : (r = lastIndex) ? "U" : (c = 0) ? "R" : (c = lastIndex) ? "L" : ""
t := Board[r, c]
if (t && t<>"??" && t<>"?")
symbol.Push(t)
BlackBox([r, c, dir])
mapBoard()
}
;--------------------------------------------------------------------------------------
BlackBox(Coord){
global
end := Ray(Coord)
r := Coord.1, c := Coord.2
endR := end.1, endC := end.2
if (end.3 = "hit")
Board[r, c] := "??" ; Hit
else if (r = endR && c = endC)
Board[r, c] := "?" ; Reflection
else if (end.3 = "miss")
{
Random, rnd, 1, % symbol.Count()
ch := symbol.RemoveAt(rnd)
Board[r, c] := ch
Board[endR, endC] := ch ; Miss
}
}
;--------------------------------------------------------------------------------------
Ray(Coord){
global
r := Coord.1, c := Coord.2, dir := Coord.3
deltaR := dir = "D" ? 1 : dir = "U" ? -1 : 0
deltaC := dir = "R" ? 1 : dir = "L" ? -1 : 0
if TroubleShooting
{
Board[r, c] := "+"
GuiControl,, % r "_" c, % "+"
Sleep 5
}
; Hit
if (dir = "R" && Mines[r, c+1])
return [r, c, "hit"]
if (dir = "L" && Mines[r, c-1])
return [r, c, "hit"]
if (dir = "U" && Mines[r-1, c])
return [r, c, "hit"]
if (dir = "D" && Mines[r+1, c])
return [r, c, "hit"]
; Deflection
if (dir = "R" && Mines[r+1, c+1])
return c=0 ? [r, c, "deflect"] : Ray([r, c, "U"]) ; right to up
if (dir = "R" && Mines[r-1, c+1])
return c=0 ? [r, c, "deflect"] : Ray([r, c, "D"]) ; right to down
if (dir = "L" && Mines[r+1, c-1])
return c=lastIndex ? [r, c, "deflect"] : Ray([r, c, "U"]) ; left to up
if (dir = "L" && Mines[r-1, c-1])
return c=lastIndex ? [r, c, "deflect"] : Ray([r, c, "D"]) ; left to down
if (dir = "U" && Mines[r-1, c+1])
return r=lastIndex ? [r, c, "deflect"] : Ray([r, c, "L"]) ; up to left
if (dir = "U" && Mines[r-1, c-1])
return r=lastIndex ? [r, c, "deflect"] : Ray([r, c, "R"]) ; up to down
if (dir = "D" && Mines[r+1, c+1])
return r=0 ? [r, c, "deflect"] : Ray([r, c, "L"]) ; down to left
if (dir = "D" && Mines[r+1, c-1])
return r=0 ? [r, c, "deflect"] : Ray([r, c, "R"]) ; down to right
r += deltaR, c += deltaC ; advance
; Miss
if (r=0 || r=lastIndex || c=0 || c=lastIndex)
return [r, c, "miss"]
return Ray([r, c, dir])
}
;--------------------------------------------------------------------------------------
Alt:: ; for troubleshooting purposes only ;-)
TroubleShooting := !TroubleShooting
Gui, show,, % TroubleShooting ? "Black Box - TroubleShooting Mode" : "Black Box"
return
;--------------------------------------------------------------------------------------
FreeBASIC
Dim Shared b(99) As String ' displayed board
Dim Shared h(99) As String ' hidden atoms
Dim Shared wikiGame As Boolean
wikiGame = False ' set to false for a 'random' game
#define atCorner(ix) ix = 0 Or ix = 9 Or ix = 90 Or ix = 99
#define inRange(ix) ix >= 1 And ix <= 98 And ix <> 9 And ix <> 90
#define atTop(ix) ix >= 1 And ix <= 8
#define atBottom(ix) ix >= 91 And ix <= 98
#define atLeft(ix) inRange(ix) And ix Mod 10 = 0
#define atRight(ix) inRange(ix) And ix Mod 10 = 9
#define inMiddle(ix) inRange(ix) And Not atTop(ix) And Not atBottom(ix) And Not atLeft(ix) And Not atRight(ix)
Sub OcultarAtomos
Dim As Integer metido = 0
While metido < 4
Dim As Integer a = Int(Rnd(1) * 78) + 11 ' 11 to 88 inclusive
Dim As Integer m = a Mod 10
If m = 0 Or m = 9 Or h(a) = "T" Then Continue While
h(a) = "T"
metido += 1
Wend
End Sub
Sub Intro
Cls
Color 15, 0
For i As Integer = 0 To 99
b(i) = " "
h(i) = "F"
Next i
If wikiGame Then
OcultarAtomos()
Else
h(32) = "T"
h(37) = "T"
h(64) = "T"
h(87) = "T"
End If
Print Spc(5); " === BLACK BOX ==="
Print !"\n H Hit (scores 1)"
Print " R Reflection (scores 1)"
Print " 1-9, Detour (scores 2)"
Print " a-c Detour for 10-12 (scores 2)"
Print " G Guess (maximum 4)"
Print " Y Correct guess"
Print " N Incorrect guess (scores 5)"
Print " A Unguessed atom"
Color 14, 0
Print !"\n Cells are numbered a0 to j9."
Print " Corner cells do nothing."
Print " Use edge cells to fire beam."
Print " Use middle cells to add/delete a guess."
Print " Game ends automatically after 4 suposicion."
Print " Enter q to abort game at any time."
End Sub
Sub DibujarCuadricula(puntos As Integer, suposicion As Integer)
Print " 0 1 2 3 4 5 6 7 8 9 "
Print " "; Chr(201); String(3, Chr(205)); Chr(203); String(3, Chr(205)); Chr(203); String(3, Chr(205)); Chr(203); String(3, Chr(205)); Chr(203); String(3, Chr(205)); Chr(203); String(3, Chr(205)); Chr(203); String(3, Chr(205)); Chr(203); String(3, Chr(205)); Chr(187)
Print "a "; Chr(186); " "; b(0); " "; Chr(186); " "; b(1); " "; Chr(186); " "; b(2); " "; Chr(186); " "; b(3); " "; Chr(186); " "; b(4); " "; Chr(186); " "; b(5); " "; Chr(186); " "; b(6); " "; Chr(186); " "; b(7); " "; Chr(186); " "; b(8)
Print " "; Chr(201); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(187)
Print "b "; Chr(186); " "; b(10); " "; Chr(186); " "; b(11); " "; Chr(186); " "; b(12); " "; Chr(186); " "; b(13); " "; Chr(186); " "; b(14); " "; Chr(186); " "; b(15); " "; Chr(186); " "; b(16); " "; Chr(186); " "; b(17); " "; Chr(186); " "; b(18); " "; Chr(186); " "; b(19); " "; Chr(186)
Print " "; Chr(204); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(185)
Print "c "; Chr(186); " "; b(20); " "; Chr(186); " "; b(21); " "; Chr(186); " "; b(22); " "; Chr(186); " "; b(23); " "; Chr(186); " "; b(24); " "; Chr(186); " "; b(25); " "; Chr(186); " "; b(26); " "; Chr(186); " "; b(27); " "; Chr(186); " "; b(28); " "; Chr(186); " "; b(29); " "; Chr(186)
Print " "; Chr(204); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(185)
Print "d "; Chr(186); " "; b(30); " "; Chr(186); " "; b(31); " "; Chr(186); " "; b(32); " "; Chr(186); " "; b(33); " "; Chr(186); " "; b(34); " "; Chr(186); " "; b(35); " "; Chr(186); " "; b(36); " "; Chr(186); " "; b(37); " "; Chr(186); " "; b(38); " "; Chr(186); " "; b(39); " "; Chr(186)
Print " "; Chr(204); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(185)
Print "e "; Chr(186); " "; b(40); " "; Chr(186); " "; b(41); " "; Chr(186); " "; b(42); " "; Chr(186); " "; b(43); " "; Chr(186); " "; b(44); " "; Chr(186); " "; b(45); " "; Chr(186); " "; b(46); " "; Chr(186); " "; b(47); " "; Chr(186); " "; b(48); " "; Chr(186); " "; b(49); " "; Chr(186)
Print " "; Chr(204); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(185)
Print "f "; Chr(186); " "; b(50); " "; Chr(186); " "; b(41); " "; Chr(186); " "; b(52); " "; Chr(186); " "; b(53); " "; Chr(186); " "; b(54); " "; Chr(186); " "; b(55); " "; Chr(186); " "; b(56); " "; Chr(186); " "; b(57); " "; Chr(186); " "; b(58); " "; Chr(186); " "; b(59); " "; Chr(186)
Print " "; Chr(204); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(185)
Print "g "; Chr(186); " "; b(60); " "; Chr(186); " "; b(61); " "; Chr(186); " "; b(62); " "; Chr(186); " "; b(63); " "; Chr(186); " "; b(64); " "; Chr(186); " "; b(65); " "; Chr(186); " "; b(66); " "; Chr(186); " "; b(67); " "; Chr(186); " "; b(68); " "; Chr(186); " "; b(69); " "; Chr(186)
Print " "; Chr(204); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(185)
Print "h "; Chr(186); " "; b(70); " "; Chr(186); " "; b(71); " "; Chr(186); " "; b(72); " "; Chr(186); " "; b(73); " "; Chr(186); " "; b(74); " "; Chr(186); " "; b(75); " "; Chr(186); " "; b(76); " "; Chr(186); " "; b(77); " "; Chr(186); " "; b(78); " "; Chr(186); " "; b(79); " "; Chr(186)
Print " "; Chr(204); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(185)
Print "i "; Chr(186); " "; b(80); " "; Chr(186); " "; b(81); " "; Chr(186); " "; b(82); " "; Chr(186); " "; b(83); " "; Chr(186); " "; b(84); " "; Chr(186); " "; b(85); " "; Chr(186); " "; b(86); " "; Chr(186); " "; b(87); " "; Chr(186); " "; b(88); " "; Chr(186); " "; b(89); " "; Chr(186)
Print " "; Chr(200); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(206); String(3, Chr(205)); Chr(188)
Print "j "; Chr(186); " "; b(90); " "; Chr(186); " "; b(91); " "; Chr(186); " "; b(92); " "; Chr(186); " "; b(93); " "; Chr(186); " "; b(94); " "; Chr(186); " "; b(95); " "; Chr(186); " "; b(96); " "; Chr(186); " "; b(97); " "; Chr(186); " "; b(98)
Print " "; Chr(200); String(3, Chr(205)); Chr(202); String(3, Chr(205)); Chr(202); String(3, Chr(205)); Chr(202); String(3, Chr(205)); Chr(202); String(3, Chr(205)); Chr(202); String(3, Chr(205)); Chr(202); String(3, Chr(205)); Chr(202); String(3, Chr(205)); Chr(188)
Color 10,0
Print !"\n Score = "; puntos; " Guesses = "; suposicion; " Status = "; Iif(suposicion <> 4, "In play", "Game over!")
Color 15, 0
Print
End Sub
Function SgteCelda() As Integer
Dim ix As Integer
Do
Dim As String sq
Input " Choose cell : ", sq
sq = Lcase(sq)
If Len(sq) = 1 And sq = "q" Then End
If Len(sq) <> 2 Or Instr("abcdefghij", Left(sq, 1)) = 0 Or Instr("0123456789", Right(sq, 1)) = 0 Then Continue Do
ix = (Asc(Left(sq, 1)) - 97) * 10 + Val(Right(sq, 1))
If atCorner(ix) Then Continue Do
Exit Do
Loop
Print
Return ix
End Function
Sub Puntuacion(puntos As Integer, suposicion As Integer)
For i As Integer = 11 To 88
Dim As Integer m = i Mod 10
If m = 0 Or m = 9 Then Continue For
If b(i) = "G" And h(i) = "T" Then
b(i) = "Y"
Elseif b(i) = "G" And h(i) = "F" Then
b(i) = "N"
puntos += 5
Elseif b(i) = " " And h(i) = "T" Then
b(i) = "A"
End If
Next i
DibujarCuadricula(puntos, suposicion)
End Sub
Sub MenuPrincipal
Dim As Integer puntos = 0
Dim As Integer suposicion = 0
Dim As String num = "0"
Color 7, 0
Do
Dim As Integer externo = 0
DibujarCuadricula(puntos, suposicion)
Dim As Integer ix = SgteCelda()
If Not inMiddle(ix) And b(ix) <> " " Then Continue Do ' already processed
Dim As Integer inc, def
If atTop(ix) Then
inc = 10
def = 1
Elseif atBottom(ix) Then
inc = -10
def = 1
Elseif atLeft(ix) Then
inc = 1
def = 10
Elseif atRight(ix) Then
inc = -1
def = 10
Else
If b(ix) <> "G" Then
b(ix) = "G"
suposicion += 1
If suposicion = 4 Then Exit Do
Else
b(ix) = " "
suposicion -= 1
End If
Continue Do
End If
Dim As Integer x = ix + inc
Dim As Integer first = -1
While inMiddle(x)
If h(x) = "T" Then ' hit
b(ix) = "H"
puntos += 1
first = 0
externo = -1
Exit While
End If
If first And (inMiddle(x+def) And h(x+def) = "T") Or (inMiddle(x-def) And h(x-def) = "T") Then ' reflection
b(ix) = "R"
puntos += 1
first = 0
externo = -1
Exit While
End If
first = 0
Dim As Integer y = x + inc - def
If inMiddle(y) And h(y) = "T" Then ' deflection
If Abs(inc) = 1 Then
inc = 10
def = 1
Elseif Abs(inc) = 10 Then
inc = 1
def = 10
End If
End If
y = x + inc + def
If inMiddle(y) And h(y) = "T" Then ' deflection or double deflection
If Abs(inc) = 1 Then
inc = -10
def = 1
Elseif Abs(inc) = 10 Then
inc = -1
def = 10
End If
End If
x += inc
Wend
If externo Then Continue Do
num = Iif(num <> "9", Chr(Asc(num) + 1), "a")
If b(ix) = " " Then puntos += 1
b(ix) = num
If inRange(x) Then
If ix = x Then
b(ix) = "R"
Else
If b(x) = " " Then puntos += 1
b(x) = num
End If
End If
Loop
DibujarCuadricula(puntos, suposicion)
Puntuacion(puntos, suposicion)
End Sub
'--- Programa Principal ---
Dim As String yn
Intro
Sleep
Do
Cls
MenuPrincipal
Color 15
Input " Play again y/n : ", yn
Loop Until Lcase(yn) = "n"
End
'--------------------------
- Output:
Similar as Wren entry.
Go
Terminal based game.
Just the basic configuration - 4 atoms in an 8 x 8 grid.
To test it against known output (as opposed to playing a sensible game), the program has been fixed (wikiGame = true) to reproduce the atom position in the Wikipedia article's example game, followed by a complete set of beams and one incorrect and three correct guesses.
Set wikiGame to false to play a normal 'random' game.
package main
import (
"bufio"
"fmt"
"log"
"math/rand"
"os"
"strings"
"time"
)
var (
b = make([]rune, 100) // displayed board
h = make([]rune, 100) // hidden atoms
scanner = bufio.NewScanner(os.Stdin)
wikiGame = true // set to false for a 'random' game
)
func initialize() {
for i := 0; i < 100; i++ {
b[i] = ' '
h[i] = 'F'
}
if !wikiGame {
hideAtoms()
} else {
h[32] = 'T'
h[37] = 'T'
h[64] = 'T'
h[87] = 'T'
}
fmt.Println(`
=== BLACK BOX ===
H Hit (scores 1)
R Reflection (scores 1)
1-9, Detour (scores 2)
a-c Detour for 10-12 (scores 2)
G Guess (maximum 4)
Y Correct guess
N Incorrect guess (scores 5)
A Unguessed atom
Cells are numbered a0 to j9.
Corner cells do nothing.
Use edge cells to fire beam.
Use middle cells to add/delete a guess.
Game ends automatically after 4 guesses.
Enter q to abort game at any time.
`)
}
func drawGrid(score, guesses int) {
fmt.Printf(" 0 1 2 3 4 5 6 7 8 9 \n")
fmt.Printf("\n")
fmt.Printf(" ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗\n")
fmt.Printf("a %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c\n",
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9])
fmt.Printf(" ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗\n")
fmt.Printf("b ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n",
b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19])
fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n")
fmt.Printf("c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n",
b[20], b[21], b[22], b[23], b[24], b[25], b[26], b[27], b[28], b[29])
fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n")
fmt.Printf("d ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n",
b[30], b[31], b[32], b[33], b[34], b[35], b[36], b[37], b[38], b[39])
fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n")
fmt.Printf("e ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n",
b[40], b[41], b[42], b[43], b[44], b[45], b[46], b[47], b[48], b[49])
fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n")
fmt.Printf("f ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n",
b[50], b[51], b[52], b[53], b[54], b[55], b[56], b[57], b[58], b[59])
fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n")
fmt.Printf("g ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n",
b[60], b[61], b[62], b[63], b[64], b[65], b[66], b[67], b[68], b[69])
fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n")
fmt.Printf("h ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n",
b[70], b[71], b[72], b[73], b[74], b[75], b[76], b[77], b[78], b[79])
fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n")
fmt.Printf("i ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n",
b[80], b[81], b[82], b[83], b[84], b[85], b[86], b[87], b[88], b[89])
fmt.Printf(" ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝\n")
fmt.Printf("j %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c\n",
b[90], b[91], b[92], b[93], b[94], b[95], b[96], b[97], b[98], b[99])
fmt.Printf(" ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝\n")
status := "In play"
if guesses == 4 {
status = "Game over!"
}
fmt.Println("\n Score =", score, "\tGuesses =", guesses, "\t Status =", status, "\n")
}
func hideAtoms() {
placed := 0
for placed < 4 {
a := 11 + rand.Intn(78) // 11 to 88 inclusive
m := a % 10
if m == 0 || m == 9 || h[a] == 'T' {
continue
}
h[a] = 'T'
placed++
}
}
func nextCell() int {
var ix int
for {
fmt.Print(" Choose cell : ")
scanner.Scan()
sq := strings.ToLower(scanner.Text())
if len(sq) == 1 && sq[0] == 'q' {
log.Fatal("program aborted")
}
if len(sq) != 2 || sq[0] < 'a' || sq[0] > 'j' || sq[1] < '0' || sq[1] > '9' {
continue
}
ix = int((sq[0]-'a')*10 + sq[1] - 48)
if atCorner(ix) {
continue
}
break
}
check(scanner.Err())
fmt.Println()
return ix
}
func atCorner(ix int) bool { return ix == 0 || ix == 9 || ix == 90 || ix == 99 }
func inRange(ix int) bool { return ix >= 1 && ix <= 98 && ix != 9 && ix != 90 }
func atTop(ix int) bool { return ix >= 1 && ix <= 8 }
func atBottom(ix int) bool { return ix >= 91 && ix <= 98 }
func atLeft(ix int) bool { return inRange(ix) && ix%10 == 0 }
func atRight(ix int) bool { return inRange(ix) && ix%10 == 9 }
func inMiddle(ix int) bool {
return inRange(ix) && !atTop(ix) && !atBottom(ix) && !atLeft(ix) && !atRight(ix)
}
func play() {
score, guesses := 0, 0
num := '0'
outer:
for {
drawGrid(score, guesses)
ix := nextCell()
if !inMiddle(ix) && b[ix] != ' ' { // already processed
continue
}
var inc, def int
switch {
case atTop(ix):
inc, def = 10, 1
case atBottom(ix):
inc, def = -10, 1
case atLeft(ix):
inc, def = 1, 10
case atRight(ix):
inc, def = -1, 10
default:
if b[ix] != 'G' {
b[ix] = 'G'
guesses++
if guesses == 4 {
break outer
}
} else {
b[ix] = ' '
guesses--
}
continue
}
var x int
first := true
for x = ix + inc; inMiddle(x); x += inc {
if h[x] == 'T' { // hit
b[ix] = 'H'
score++
first = false
continue outer
}
if first && (inMiddle(x+def) && h[x+def] == 'T') ||
(inMiddle(x-def) && h[x-def] == 'T') { // reflection
b[ix] = 'R'
score++
first = false
continue outer
}
first = false
y := x + inc - def
if inMiddle(y) && h[y] == 'T' { // deflection
switch inc {
case 1, -1:
inc, def = 10, 1
case 10, -10:
inc, def = 1, 10
}
}
y = x + inc + def
if inMiddle(y) && h[y] == 'T' { // deflection or double deflection
switch inc {
case 1, -1:
inc, def = -10, 1
case 10, -10:
inc, def = -1, 10
}
}
}
if num != '9' {
num++
} else {
num = 'a'
}
if b[ix] == ' ' {
score++
}
b[ix] = num
if inRange(x) {
if ix == x {
b[ix] = 'R'
} else {
if b[x] == ' ' {
score++
}
b[x] = num
}
}
}
drawGrid(score, guesses)
finalScore(score, guesses)
}
func check(err error) {
if err != nil {
log.Fatal(err)
}
}
func finalScore(score, guesses int) {
for i := 11; i <= 88; i++ {
m := i % 10
if m == 0 || m == 9 {
continue
}
if b[i] == 'G' && h[i] == 'T' {
b[i] = 'Y'
} else if b[i] == 'G' && h[i] == 'F' {
b[i] = 'N'
score += 5
} else if b[i] == ' ' && h[i] == 'T' {
b[i] = 'A'
}
}
drawGrid(score, guesses)
}
func main() {
rand.Seed(time.Now().UnixNano())
for {
initialize()
play()
inner:
for {
fmt.Print(" Play again y/n : ")
scanner.Scan()
yn := strings.ToLower(scanner.Text())
switch yn {
case "n":
return
case "y":
break inner
}
}
check(scanner.Err())
}
}
- Output:
As the grid is displayed 29 times in all, this has been abbreviated to show just the first 2 and the last 3.
=== BLACK BOX === H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time. 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 0 Guesses = 0 Status = In play Choose cell : b0 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 2 Guesses = 0 Status = In play Choose cell : c0 ................ (Screens 3 to 26 omitted) ................ Score = 32 Guesses = 2 Status = In play Choose cell : g4 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ H ║ 9 ║ H ║ 7 ║ 9 ║ H ║ 8 ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ 8 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ H ║ G ║ ║ ║ ║ ║ ║ G ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ 3 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ 4 ║ ║ ║ ║ ║ ║ ║ ║ ║ 7 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ H ║ ║ ║ ║ G ║ ║ ║ ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ 5 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ H ║ ║ ║ ║ ║ ║ ║ ║ ║ H ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ 3 ║ H ║ 5 ║ H ║ 4 ║ R ║ H ║ R ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 32 Guesses = 3 Status = In play Choose cell : i7 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ H ║ 9 ║ H ║ 7 ║ 9 ║ H ║ 8 ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ 8 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ H ║ G ║ ║ ║ ║ ║ ║ G ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ 3 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ 4 ║ ║ ║ ║ ║ ║ ║ ║ ║ 7 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ H ║ ║ ║ ║ G ║ ║ ║ ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ 5 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ H ║ ║ ║ ║ ║ ║ ║ G ║ ║ H ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ 3 ║ H ║ 5 ║ H ║ 4 ║ R ║ H ║ R ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 32 Guesses = 4 Status = Game over! 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ H ║ 9 ║ H ║ 7 ║ 9 ║ H ║ 8 ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ 8 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ H ║ N ║ A ║ ║ ║ ║ ║ Y ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ 3 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ 4 ║ ║ ║ ║ ║ ║ ║ ║ ║ 7 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ H ║ ║ ║ ║ Y ║ ║ ║ ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ 5 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ H ║ ║ ║ ║ ║ ║ ║ Y ║ ║ H ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ 3 ║ H ║ 5 ║ H ║ 4 ║ R ║ H ║ R ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 37 Guesses = 4 Status = Game over! Play again y/n : n
J
Requires a recent (release 9) jqt:
require'ide/qt/gl2'
coinsert'jgl2'
NB. event handlers
game_board_mbldown=: {{
xy=.<.40%~2{._ ".sysdata
if. xy e. EDGES do.
probe xy
elseif. xy e. BLACK do.
guess xy
end.
draw''
}}
game_finish_button=: {{ draw FINISHED=: 1 [BEAM=: EMPTY }}
NB. support code
crds=: 2 {."1 ]
dirs=:_2 {."1 ]
probe=: {{
BEAM=: ,:y
dir=. DIRS{~EDGES i.y
while. -. ({:BEAM) e. ATOMS do.
bar=. (#~ 1-0 e.,@dirs)MIRRORS#~(crds MIRRORS) e. _1{.BEAM
if. 1=#bar do. dir=.{.(/: |@j./"1) dir(+,:-),dirs bar
elseif. 2=#bar do. dir=. -dir
end.
BEAM=: BEAM,dir+{:BEAM
if. -.({:BEAM) e. BLACK do. break. end.
end.
if. 1 e. BEAM e. BLACK do.
select. #e=.BEAM([-.-.)EDGES
case. 1 do. BEAM remember 'H'
case. 2 do. BEAM remember (#~.e){::'?';'R';0
end.
else.
(BEAM=:1{.BEAM) remember 'R'
end.
}}
remember=: {{
ndx=. EDGES i.x([-.-.)EDGES
if. 0=y do. y=. ":{.(0-.~~.0,,0".&>ndx{LABELS),1+>./0,,0".&>LABELS end.
LABELS=: (<y) ndx} LABELS
}}
guess=: {{ if.-.FINISHED do. GUESSES=: GUESSES ,`-.@.(e.~) y end. }}
NB. rendering
bbox=: {{
4 bbox y
:
wd{{)n
pc game closeok;
cc message static center;
cc board isidraw;
set board wh SZ SZ;
cc finish button;
pshow;
}} rplc 'SZ';":40*2+SIZE=:y
BLACK=: ,/1+DIM#:i.DIM=:,~SIZE
EDGES=: (,/(2+DIM)#:i.2+DIM)-.BLACK,>,{;~0 1*DIM+1
DIRS=: (1+SIZE) ((*@| |."1) * _1^=) EDGES
LABELS=: (#EDGES)#a:
ATOMS=: ({~ x?#) BLACK
MIRRORS=: /:~,/ATOMS(+,])"1/0 0-.~>,{;~i:1
GUESSES=: EMPTY
BEAM=: EMPTY
FINISHED=: 0
draw''
}}
boxes=: {{ 40*4{.!.1"1 y }}
drawatoms=: {{ glellipse 5 5 _10 _10+"1]boxes y[glpen glbrush glrgb x }}
draw=: {{
glclear''
glfont '"Lucidia Console" 15' [gltextcolor glrgb 0 255 255 NB. yellow
glpen 2 1[glrgb 3#255 NB. white
glrect boxes EDGES [glbrush glrgb 184 0 0 NB. dark red
glrect boxes BLACK [glbrush glrgb 0 0 0
wd 'set message text ',N,&":' point','s'#~1~:N=.(FINISHED*5*#GUESSES-.ATOMS)++/LABELS~:a:
if. FINISHED do.
255 0 0 drawatoms GUESSES -. ATOMS
0 255 0 drawatoms ATOMS([-.-.)GUESSES
0 0 255 drawatoms ATOMS-.GUESSES
if.#BEAM do.
gllines 20+,40*BEAM [glpen 2 1 [glbrush glrgb 255 255 0
glellipse 15 15 10 10+4{.40*{.BEAM
end.
else.
128 128 128 drawatoms GUESSES
end.
(10+40*EDGES) {{ gltext;y [ gltextxy x }}"_1 LABELS
wd 'set finish enable ',":FINISHED<GUESSES=&#ATOMS
glpaint''
}}
Example use:
bbox 8
. Or, for 10 atoms in a 15x15 grid:
10 bbox 15
.
This version allows the user to place and observe individual beams after finishing a game.
JavaScript
Play it here.
var sel, again, check, score, done, atoms, guesses, beamCnt, brdSize;
function updateScore( s ) {
score += s || 0;
para.innerHTML = "Score: " + score;
}
function checkIt() {
check.className = "hide";
again.className = "again";
done = true;
var b, id;
for( var j = 0; j < brdSize; j++ ) {
for( var i = 0; i < brdSize; i++ ) {
if( board[i][j].H ) {
b = document.getElementById( "atom" + ( i + j * brdSize ) );
b.innerHTML = "⚈";
if( board[i][j].T ) {
b.style.color = "#0a2";
} else {
b.style.color = "#f00";
updateScore( 5 );
}
}
}
}
}
function isValid( n ) {
return n > -1 && n < brdSize;
}
function stepBeam( sx, sy, dx, dy ) {
var s = brdSize - 2
if( dx ) {
if( board[sx][sy].H ) return {r:"H", x:sx, y:sy};
if( ( (sx == 1 && dx == 1) || (sx == s && dx == -1) ) && ( ( sy > 0 && board[sx][sy - 1].H ) ||
( sy < s && board[sx][sy + 1].H ) ) ) return {r:"R", x:sx, y:sy};
if( isValid( sx + dx ) ) {
if( isValid( sy - 1 ) && board[sx + dx][sy - 1].H ) {
dx = 0; dy = 1;
}
if( isValid( sy + 1 ) && board[sx + dx][sy + 1].H ) {
dx = 0; dy = -1;
}
sx += dx;
return stepBeam( sx, sy, dx, dy );
} else {
return {r:"O", x:sx, y:sy};
}
} else {
if( board[sx][sy].H ) return {r:"H", x:sx, y:sy};
if( ( (sy == 1 && dy == 1) || (sy == s && dy == -1) ) && ( ( sx > 0 && board[sx - 1][sy].H ) ||
( sx < s && board[sx + 1][sy].H ) ) ) return {r:"R", x:sx, y:sy};
if( isValid( sy + dy ) ) {
if( isValid( sx - 1 ) && board[sx - 1][sy + dy].H ) {
dy = 0; dx = 1;
}
if( isValid( sx + 1 ) && board[sx + 1][sy + dy].H ) {
dy = 0; dx = -1;
}
sy += dy;
return stepBeam( sx, sy, dx, dy );
} else {
return {r:"O", x:sx, y:sy};
}
}
}
function fireBeam( btn ) {
var sx = btn.i, sy = btn.j, dx = 0, dy = 0;
if( sx == 0 || sx == brdSize - 1 ) dx = sx == 0 ? 1 : - 1;
else if( sy == 0 || sy == brdSize - 1 ) dy = sy == 0 ? 1 : - 1;
var s = stepBeam( sx + dx, sy + dy, dx, dy );
switch( s.r ) {
case "H":
btn.innerHTML = "H";
updateScore( 1 );
break;
case "R":
btn.innerHTML = "R";
updateScore( 1 );
break;
case "O":
if( s.x == sx && s.y == sy ) {
btn.innerHTML = "R";
updateScore( 1 );
}
else {
var b = document.getElementById( "fire" + ( s.x + s.y * brdSize ) );
btn.innerHTML = "" + beamCnt;
b.innerHTML = "" + beamCnt;
beamCnt++;
updateScore( 2 );
}
}
}
function setAtom( btn ) {
if( done ) return;
var b = document.getElementById( "atom" + ( btn.i + btn.j * brdSize ) );
if( board[btn.i][btn.j].T == 0 && guesses < atoms ) {
board[btn.i][btn.j].T = 1;
guesses++;
b.innerHTML = "⚈";
} else if( board[btn.i][btn.j].T == 1 && guesses > 0 ) {
board[btn.i][btn.j].T = 0;
guesses--;
b.innerHTML = " ";
}
if( guesses == atoms ) check.className = "check";
else check.className = "hide";
}
function startGame() {
score = 0;
updateScore();
check.className = again.className = "hide";
var e = document.getElementById( "mid" );
if( e.firstChild ) e.removeChild( e.firstChild );
brdSize = sel.value;
done = false;
if( brdSize < 5 ) return;
var brd = document.createElement( "div" );
brd.id = "board";
brd.style.height = brd.style.width = 5.2 * brdSize + "vh"
e.appendChild( brd );
var b, c, d;
for( var j = 0; j < brdSize; j++ ) {
for( var i = 0; i < brdSize; i++ ) {
b = document.createElement( "button" );
b.i = i; b.j = j;
if( j == 0 && i == 0 || j == 0 && i == brdSize - 1 ||
j == brdSize - 1 && i == 0 || j == brdSize - 1 && i == brdSize - 1 ) {
b.className = "corner";
} else {
if( j == 0 || j == brdSize - 1 || i == 0 || i == brdSize - 1 ) {
b.className = "fire";
b.id = "fire" + ( i + j * brdSize );
} else {
b.className = "atom";
b.id = "atom" + ( i + j * brdSize );
}
b.addEventListener( "click",
function( e ) {
if( e.target.className == "fire" && e.target.innerHTML == " " ) fireBeam( e.target );
else if( e.target.className == "atom" ) setAtom( e.target );
}, false );
}
b.appendChild( document.createTextNode( " " ) );
brd.appendChild( b );
}
}
board = new Array( brdSize );
for( var j = 0; j < brdSize; j++ ) {
board[j] = new Array( brdSize );
for( i = 0; i < brdSize; i++ ) {
board[j][i] = {H: 0, T: 0};
}
}
guesses = 0; beamCnt = 1;
atoms = brdSize == 7 ? 3 : brdSize == 10 ? 4 : 4 + Math.floor( Math.random() * 5 );
var s = brdSize - 2, i, j;
for( var k = 0; k < atoms; k++ ) {
while( true ) {
i = 1 + Math.floor( Math.random() * s );
j = 1 + Math.floor( Math.random() * s );
if( board[i][j].H == 0 ) break;
}
board[i][j].H = 1;
}
}
function init() {
sel = document.createElement( "select");
sel.options.add( new Option( "5 x 5 [3 atoms]", 7 ) );
sel.options.add( new Option( "8 x 8 [4 atoms]", 10 ) );
sel.options.add( new Option( "10 x 10 [4 - 8 atoms]", 12 ) );
sel.addEventListener( "change", startGame, false );
document.getElementById( "top" ).appendChild( sel );
check = document.createElement( "button" );
check.appendChild( document.createTextNode( "Check it!" ) );
check.className = "hide";
check.addEventListener( "click", checkIt, false );
again = document.createElement( "button" );
again.appendChild( document.createTextNode( "Again" ) );
again.className = "hide";
again.addEventListener( "click", startGame, false );
para = document.createElement( "p" );
para.className = "txt";
var d = document.getElementById( "bot" );
d.appendChild( para );
d.appendChild( check );
d.appendChild( again );
startGame();
}
Julia
Gtk library GUI version.
using Colors, Cairo, Graphics, Gtk
struct BoxPosition
x::Int
y::Int
BoxPosition(i = 0, j = 0) = new(i, j)
end
@enum TrialResult Miss Hit Reflect Detour
struct TrialBeam
entry::BoxPosition
exit::Union{BoxPosition, Nothing}
result::TrialResult
end
function blackboxapp(boxlength=8, boxwidth=8, numballs=4)
r, turncount, guesses, guesscount, correctguesses = 20, 0, BoxPosition[], 0, 0
showballs, boxoffsetx, boxoffsety = false, r, r
boxes = fill(colorant"wheat", boxlength + 4, boxwidth + 4)
beamhistory, ballpositions = Vector{TrialBeam}(), Vector{BoxPosition}()
win = GtkWindow("Black Box Game", 348, 800) |> (GtkFrame() |> (box = GtkBox(:v)))
settingsbox = GtkBox(:v)
playtoolbar = GtkToolbar()
newgame = GtkToolButton("New Game")
set_gtk_property!(newgame, :label, "New Game")
set_gtk_property!(newgame, :is_important, true)
reveal = GtkToolButton("Reveal")
set_gtk_property!(reveal, :label, "Reveal Box")
set_gtk_property!(reveal, :is_important, true)
map(w->push!(playtoolbar, w),[newgame, reveal])
scrwin = GtkScrolledWindow()
can = GtkCanvas()
set_gtk_property!(can, :expand, true)
map(w -> push!(box, w),[settingsbox, playtoolbar, scrwin])
push!(scrwin, can)
function newgame!(w)
empty!(ballpositions)
empty!(guesses)
empty!(beamhistory)
guessing, showballs, guesscount, correctguesses = false, false, 0, 0
fill!(boxes, colorant"wheat")
boxes[2, 3:end-2] .= boxes[end-1, 3:end-2] .= colorant"red"
boxes[3:end-2, 2] .= boxes[3:end-2, end-1] .= colorant"red"
boxes[3:end-2, 3:end-2] .= colorant"black"
while length(ballpositions) < numballs
p = BoxPosition(rand(3:boxlength+2), rand(3:boxwidth+2))
if !(p in ballpositions)
push!(ballpositions, p)
end
end
draw(can)
end
@guarded draw(can) do widget
ctx = Gtk.getgc(can)
select_font_face(ctx, "Courier", Cairo.FONT_SLANT_NORMAL, Cairo.FONT_WEIGHT_BOLD)
fontpointsize = 12
set_font_size(ctx, fontpointsize)
# print black box graphic
for i in 1:boxlength + 4, j in 1:boxwidth + 4
set_source(ctx, boxes[i, j])
move_to(ctx, boxoffsetx + i * r, boxoffsety + j * r)
rectangle(ctx, boxoffsetx + i * r, boxoffsety + j * r, r, r)
fill(ctx)
p = BoxPosition(i, j)
# show current guesses
if p in guesses
set_source(ctx, colorant"red")
move_to(ctx, boxoffsetx + i * r + 2, boxoffsety + j * r + fontpointsize)
show_text(ctx, p in ballpositions ? "+" : "-")
stroke(ctx)
end
# show ball placements if reveal -> showballs
if showballs && p in ballpositions
set_source(ctx, colorant"green")
circle(ctx, boxoffsetx + (i + 0.5) * r , boxoffsety + (j + 0.5) * r, 0.4 * r)
fill(ctx)
end
end
# draw dividing lines
set_line_width(ctx, 2)
set_source(ctx, colorant"wheat")
for i in 4:boxlength + 2
move_to(ctx, boxoffsetx + i * r, boxoffsety + 3 * r)
line_to(ctx, boxoffsetx + i * r, boxoffsety + (boxlength + 3) * r)
stroke(ctx)
end
for j in 4:boxwidth + 2
move_to(ctx, boxoffsetx + 3 * r, boxoffsety + j * r)
line_to(ctx, boxoffsetx + (boxlength + 3) * r, boxoffsety + j * r)
stroke(ctx)
end
# show scoring update
set_source(ctx, colorant"white")
rectangle(ctx, 0, 305, 400, 50)
fill(ctx)
correct, incorrect = string(correctguesses), string(guesscount - correctguesses)
score = string(2 * correctguesses - guesscount)
set_source(ctx, colorant"black")
move_to(ctx, 0, 320)
show_text(ctx, " Correct: $correct Incorrect: $incorrect Score: $score")
stroke(ctx)
# show latest trial beams and results and trial history
set_source(ctx, colorant"white")
rectangle(ctx, 0, 360, 400, 420)
fill(ctx)
set_source(ctx, colorant"black")
move_to(ctx, 0, 360)
show_text(ctx, " Test Beam History")
stroke(ctx)
move_to(ctx, 0, 360 + fontpointsize * 1.5)
show_text(ctx, " # Start Result End")
stroke(ctx)
for (i, p) in enumerate(beamhistory)
move_to(ctx, 0, 360 + fontpointsize * (i + 1.5))
set_source(ctx, colorant"black")
s = " " * rpad(i, 3) * rpad("($(p.entry.x - 2),$(p.entry.y - 2))", 8) *
rpad(p.result, 12) * (p.exit == nothing ? " " :
"($(p.exit.x - 2), $(p.exit.y - 2))")
show_text(ctx, s)
stroke(ctx)
move_to(ctx, graphicxyfrombox(p.entry, 0.5 * fontpointsize)...)
set_source(ctx, colorant"yellow")
show_text(ctx, string(i))
stroke(ctx)
if p.exit != nothing
move_to(ctx, graphicxyfrombox(p.exit, 0.5 * fontpointsize)...)
set_source(ctx, colorant"lightblue")
show_text(ctx, string(i))
stroke(ctx)
end
end
Gtk.showall(win)
end
reveal!(w) = (showballs = !showballs; draw(can); Gtk.showall(win))
boxfromgraphicxy(x, y) = Int(round(x / r - 1.5)), Int(round(y / r - 1.5))
graphicxyfrombox(p, oset) = boxoffsetx + p.x * r + oset/2, boxoffsety + p.y * r + oset * 2
dirnext(x, y, dir) = x + dir[1], y + dir[2]
rightward(d) = (-d[2], d[1])
leftward(d) = (d[2], -d[1])
rearward(direction) = (-direction[1], -direction[2])
ballfront(x, y, d) = BoxPosition(x + d[1], y + d[2]) in ballpositions
ballright(x, y, d) = BoxPosition((dirnext(x, y, d) .+ rightward(d))...) in ballpositions
ballleft(x, y, d) = BoxPosition((dirnext(x, y, d) .+ leftward(d))...) in ballpositions
twocorners(x, y, d) = !ballfront(x, y, d) && ballright(x, y, d) && ballleft(x, y, d)
enteringstartzone(x, y, direction) = atstart(dirnext(x, y, direction)...)
function atstart(x, y)
return ((x == 2 || x == boxlength + 3) && (2 < y <= boxwidth + 3)) ||
((y == 2 || y == boxwidth + 3) && (2 < x <= boxlength + 3))
end
function runpath(x, y)
startp = BoxPosition(x, y)
direction = (x == 2) ? (1, 0) : (x == boxlength + 3) ? (-1, 0) :
(y == 2) ? (0, 1) : (0, -1)
while true
if ballfront(x, y, direction)
return Hit, nothing
elseif twocorners(x, y, direction)
if atstart(x, y)
return Reflect, startp
end
direction = rearward(direction)
continue
elseif ballleft(x, y, direction)
if atstart(x, y)
return Reflect, startp
end
direction = rightward(direction)
continue
elseif ballright(x, y, direction)
if atstart(x, y)
return Reflect, startp
end
direction = leftward(direction)
continue
elseif enteringstartzone(x, y, direction)
x2, y2 = dirnext(x, y, direction)
endp = BoxPosition(x2, y2)
if x2 == startp.x && y2 == startp.y
return Reflect, endp
else
if startp.x == x2 || startp.y == y2
return Miss, endp
else
return Detour, endp
end
end
end
x, y = dirnext(x, y, direction)
@assert((2 < x < boxlength + 3) && (2 < y < boxwidth + 3))
end
end
can.mouse.button1press = @guarded (widget, event) -> begin
x, y = boxfromgraphicxy(event.x, event.y)
# get click in blackbox area as a guess
if 2 < x < boxlength + 3 && 2 < y < boxwidth + 3
p = BoxPosition(x, y)
if !(p in guesses)
push!(guesses, BoxPosition(x, y))
guesscount += 1
if p in ballpositions
correctguesses += 1
end
end
draw(can)
# test beam
elseif atstart(x, y)
result, endpoint = runpath(x, y)
push!(beamhistory, TrialBeam(BoxPosition(x, y), endpoint, result))
if length(beamhistory) > 32
popfirst!(beamhistory)
end
draw(can)
end
end
condition = Condition()
endit(w) = notify(condition)
signal_connect(endit, win, :destroy)
signal_connect(newgame!, newgame, :clicked)
signal_connect(reveal!, reveal, :clicked)
newgame!(win)
Gtk.showall(win)
wait(condition)
end
blackboxapp()
Nim
import random, sequtils, strutils
const WikiGame = true
type
Game = object
b: array[100, char] # displayed board.
h: array[100, char] # hidden atoms.
proc hideAtoms(game: var Game) =
var placed = 0
while placed < 4:
let a = rand(11..88)
let m = a mod 10
if m == 0 or m == 9 or game.h[a] == 'T':
continue
game.h[a] = 'T'
inc placed
proc initGame(): Game =
for i in 0..99:
result.b[i] = ' '
result.h[i] = 'F'
if not WikiGame:
result.hideAtoms()
else:
result.h[32] = 'T'
result.h[37] = 'T'
result.h[64] = 'T'
result.h[87] = 'T'
proc drawGrid(game: Game; score, guesses: Natural) =
echo " 0 1 2 3 4 5 6 7 8 9\n"
echo " ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗"
echo "a $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $#".format(game.b[0..9].mapIt($it))
echo " ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗"
echo "b ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[10..19].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "c ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[20..29].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "d ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[30..39].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "e ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[40..49].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "f ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[50..59].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "g ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[60..69].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "h ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[70..79].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "i ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[80..89].mapIt($it))
echo " ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝"
echo "j $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $#".format(game.b[90..99].mapIt($it))
echo " ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝"
let status = if guesses == 4: "Game over!" else: "In play"
echo "\n Score = ", score, "\tGuesses = ", guesses, "\t Status = ", status, '\n'
proc finalScore(game: var Game; score, guesses: Natural) =
var score = score
for i in 11..88:
let m = i mod 10
if m in [0, 9]: continue
if game.b[i] == 'G':
if game.h[i] == 'T':
game.b[i] = 'Y'
else:
game.b[i] = 'N'
inc score, 5
elif game.b[i] == ' ' and game.h[i] == 'T':
game.b[i] = 'A'
game.drawGrid(score, guesses)
func atCorner(ix: int): bool = ix in [0, 9, 90, 99]
func inRange(ix: int): bool = ix in 1..98 and ix notin [9, 90]
func atTop(ix: int): bool = ix in 1..8
func atBottom(ix: int): bool = ix in 91..98
func atLeft(ix: int): bool = ix.inRange and ix mod 10 == 0
func atRight(ix: int): bool = ix.inRange and ix mod 10 == 9
func inMiddle(ix: int): bool =
ix.inRange and not (ix.atTop or ix.atBottom or ix.atLeft or ix.atRight)
proc nextCell(game: Game): int =
while true:
stdout.write " Choose cell: "
stdout.flushFile()
try:
let sq = stdin.readLine().toLowerAscii
if sq == "q":
quit "Quitting.", QuitSuccess
if sq.len != 2 or sq[0] notin 'a'..'j' or sq[1] notin '0'..'9':
continue
result = int((ord(sq[0]) - ord('a')) * 10 + ord(sq[1]) - ord('0'))
if not result.atCorner: break
except EOFError:
echo()
quit "Encountered end of file. Quitting.", QuitFailure
echo()
proc play(game: var Game) =
var score, guesses = 0
var num = '0'
block outer:
while true:
block inner:
game.drawGrid(score, guesses)
let ix = game.nextCell()
if not ix.inMiddle and game.b[ix] != ' ': # already processed.
continue
var incr, def: int
if ix.atTop:
(incr, def) = (10, 1)
elif ix.atBottom:
(incr, def) = (-10, 1)
elif ix.atLeft:
(incr, def) = (1, 10)
elif ix.atRight:
(incr, def) = (-1, 10)
else:
if game.b[ix] != 'G':
game.b[ix] = 'G'
inc guesses
if guesses == 4: break outer
else:
game.b[ix] = ' '
dec guesses
continue
var first = true
var x = ix + incr
while x.inMiddle:
if game.h[x] == 'T':
# Hit.
game.b[ix] = 'H'
inc score
first = false
break inner
if first and (x + def).inMiddle and game.h[x + def] == 'T' or
(x - def).inMiddle and game.h[x - def] == 'T':
# Reflection.
game.b[ix] = 'R'
inc score
first = false
break inner
first = false
var y = x + incr - def
if y.inMiddle and game.h[y] == 'T':
# Deflection.
(incr, def) = if incr in [-1, 1]: (10, 1) else: (1, 10)
y = x + incr + def
if y.inMiddle and game.h[y] == 'T':
# Deflection or double deflection.
(incr, def) = if incr in [-1, 1]: (-10, 1) else: (-1, 10)
inc x, incr
num = if num != '9': succ(num) else: 'a'
if game.b[ix] == ' ': inc score
game.b[ix] = num
if x.inRange:
if ix == x:
game.b[ix] = 'R'
else:
if game.b[x] == ' ': inc score
game.b[x] = num
game.drawGrid(score, guesses)
game.finalScore(score, guesses)
proc main() =
randomize()
while true:
var game = initGame()
echo """
=== BLACK BOX ===
H Hit (scores 1)
R Reflection (scores 1)
1-9, Detour (scores 2)
a-c Detour for 10-12 (scores 2)
G Guess (maximum 4)
Y Correct guess
N Incorrect guess (scores 5)
A Unguessed atom
Cells are numbered a0 to j9.
Corner cells do nothing.
Use edge cells to fire beam.
Use middle cells to add/delete a guess.
Game ends automatically after 4 guesses.
Enter q to abort game at any time.
"""
game.play()
while true:
stdout.write " Play again (y/n): "
stdout.flushFile()
case stdin.readLine().toLowerAscii()
of "n": return
of "y": break
main()
- Output:
Using same input as in Wren entry with WikiGame = true
:
=== BLACK BOX === H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time. 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 0 Guesses = 0 Status = In play Choose cell: b0 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 2 Guesses = 0 Status = In play Choose cell: c0 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 0 Status = In play Choose cell: d7 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 1 Status = In play Choose cell: d4 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ G ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 2 Status = In play Choose cell: e3 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ G ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ G ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 3 Status = In play Choose cell: h2 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ G ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ G ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ G ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 4 Status = Game over! 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ A ║ ║ N ║ ║ ║ Y ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ N ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ A ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ N ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ A ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 19 Guesses = 4 Status = Game over! Play again (y/n): n
Phix
A configurable GUI version of the Black Box game, with a Knuth solver/helper.
-- demo\rosetta\Black_Box.exw
constant title = "Black Box",
help_text = """
Discover the location of objects/atoms using the fewest probes/rays.
See distributed version for much longer help text and other comments.
"""
integer size, -- eg 8
s1, s2, -- size+1|2
count, -- eg 4
mask -- eg #0b100000 (first such >size^2-count+1)
-- Note that new_game() contains limiting code.
sequence gameboard, -- actual, count 1's and size*size-count 0's.
eboard, -- one of "", as being enumerated through
results, -- results of rays/probes, {x,y,c,x,y} format
guessxy, -- locations (each element is {x,y})
guessclr, -- colours of "" (CD_BLUE for a guess,
-- CD_GREEN for correct,
-- CD_RED for wrong,
-- CD_YELLOW/CYAN for hints.
hidden, -- "" as saved during setup
possibles, -- up to 635,376 integer codes for 8*8 with 4 game,
-- each entry being possible for content of results,
-- but never deliberately driven over 100,000.
knowns, -- these "are" atoms (but "maybe" if tried<maxtry)
minmaxmove -- best move available, see minmaxcount
integer possible, -- # of possibles checked to be plausible(), ie
-- [posssible+1..$] are all subject to imminent
-- deletion by the idle handler, if invalid.
hinted, -- # of probes analysed by hint_explore().
minmaxcount -- best (so far)
atom tried, maxtry -- # of enumerations attempted/theoretical max.
bool hints_used = false -- (affects the scoring)
function probe(integer x, y, sequence board, bool bSort=true)
--
-- returns {x,y,c,rx,ry} primarily for use in redraw_cb(), and
-- secondarily for use in plausible().
-- where c is: -1 for reflection, 0 for hit, and +1 otherwise.
-- Note that for the latter you need to allocate an actual colour
-- elsewhere (if this did that it would spanner plausible() etc),
-- and also note that -2 is now in use for the ray/probe hint.
-- Also x,y and rx,ry re-ordered lowest-first to avoid duplicates,
-- except for hint exploration, which passes a bSort of false.
--
integer rx = x, ry = y, -- current/emerge point (ray)
dx = 0, dy = 0, -- direction of travel
moves = 0 -- debug aid
if x=0 then dx = +1 -- left entry, moving right
elsif y=0 then dy = +1 -- top " down
elsif x=s1 then dx = -1 -- right " left
elsif y=s1 then dy = -1 -- btm " up
else ?9/0 -- (sanity check)
end if
while true do
integer nx = rx+dx, -- next logical position
ny = ry+dy,
idx = (ny-1)*size+nx
if nx=0 or nx=s1 or ny=0 or ny=s1 then
if x=nx and y=ny then
return {x,y,-1,0,0} -- Reflection
elsif bSort then
{{x,y},{nx,ny}} = sort({{x,y},{nx,ny}})
end if
return {x,y,1,nx,ny} -- Emerges here
elsif idx<=0 then
?9/0 -- (sanity check)
elsif board[idx] then
return {x,y,0,0,0} -- Hit
--
-- aside: rather than check diagonally, nx/ny are
-- simply discarded when a deflection occurs,
-- and we actually check things laterally.
--
elsif dx=0 then
-- up/down movement, check sides
if nx>1 and board[idx-1] then
if nx<size and board[idx+1] then
dy = -dy -- 180
else
{dx,dy} = {1,0} -- right
-- (yep, both up & down deflected
-- right by an atom on the left)
end if
elsif nx<size and board[idx+1] then
{dx,dy} = {-1,0} -- left
-- (ditto left by one on the right)
else
{rx,ry} = {nx,ny}
end if
elsif dy=0 then
-- left/right movement, check above/below
if ny>1 and board[idx-size] then
if ny<size and board[idx+size] then
dx = -dx -- 180
else
{dx,dy} = {0,1} -- down
-- (yep, left & right are both
-- deflected down by one above)
end if
elsif ny<size and board[idx+size] then
{dx,dy} = {0,-1} -- up
-- (ditto up by one below)
else
{rx,ry} = {nx,ny}
end if
else
?9/0 -- (sanity check, dx,dy=={0,0}!?)
end if
if rx=0 or rx=s1 or ry=0 or ry=s1 then
{dx,dy} = {0,0} -- (outer swivel===reflection)
end if
-- guard against infinite loops, why not.
-- *2 because swivel/move counted separately.
moves += 1
if moves>2*size*size then ?9/0 end if
end while
end function
function plausible(sequence board)
for i=1 to length(results) do
sequence ri = results[i]
integer {x,y} = ri
if probe(x,y,board)!=ri then return false end if
end for
return true
end function
--
-- For the smaller games we could use almost any storage method, but to facilitate larger
-- boards with more atoms we should be as stingy with memory as possible. To that end an
-- enumeration is stored as a compact set of offsets to the next piece. For instance the
-- board {0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,....1} is stored as offsets {4,6,8,64-18}
-- further using an appropriate mask to give (((((46*#40)+8)*#40)+6)*#40)+4 which can be
-- stored as a single integer/atom, yet unpacked quite easily, see next. Note there is
-- code in new_game(), for valuechanged_cb(), that ensures we can store count*bits, and
-- more by luck than judgement that (partly) helps avoid configurations that would take
-- far longer than the universe has existed to enumerate and scan even just the once.
--
function unpack(atom code)
sequence board = repeat(0,size*size)
integer offset = 0, r, check = 0
while code do
r = remainder(code,mask)
if r<=0 then ?9/0 end if -- sanity check
offset += r
board[offset] = 1
code = floor(code/mask)
check += 1
end while
if check!=count then ?9/0 end if -- sanity check
return board
end function
function pack(sequence board)
atom code = 0, pmask = 1
integer idx = 0, check = 0
while true do
integer prev = idx
idx = find(1,board,idx+1)
if idx=0 then exit end if
code = code + (idx-prev)*pmask
check += 1
pmask *= mask
end while
if check!=count then ?9/0 end if -- sanity check
-- if unpack(code)!=board then ?9/0 end if -- ""
return code
end function
procedure trim_possibles()
--
-- Re-process the possibles table as follows:
-- 111...222322323...$
-- where 111... is possibly empty ok [1..possible],
-- and 222322323 is some chunk [possible+1..limit],
-- with 2s for oks and 3s for now-failing entries,
-- which gets processed in a right-to-left order,
-- such that fails(3) get replaced from the (1)s,
-- being careful to quit early on any overlap, and
-- /or re-test same slot if the 111... exhausted.
-- Finally, trim off the dead head of possibles[].
-- The result is quite scrambled, but care we not.
--
integer limit = min(possible+100_000,length(possibles)),
limit0 = limit,
kill = 1 -- (actually 1 over)
while limit>max(possible,kill-1) do
if not plausible(unpack(possibles[limit])) then
possibles[limit] = possibles[kill]
if kill<=possible then
limit -= 1
end if
kill += 1
else
limit -= 1
end if
end while
possibles = possibles[kill..$]
possible = limit0-kill+1
end procedure
procedure enumerate()
atom limit = min(tried+100_000,maxtry)
while tried<limit and length(possibles)<100_000 do
tried += 1
if plausible(eboard) then
possibles &= pack(eboard)
possible += 1
end if
--
-- think abacus: find the first bead you can shift left,
-- and slam the rest of them hard right.
-- similar to binary counting, but you must always have
-- exactly 'count' beads (ie 1's), eg
-- choose(2*2,2) is 6:
-- 0b0011 0b0101 0b0110 0b1001 0b1010 0b1100
--
-- However, because we are scanning from top left down
-- to bottom right, it turned out better to do them in
-- reverse order, hence shift right and slam left (not
-- quite an exact mirror, but close enough).
--
integer idx = find(1,eboard), last = 1
while true do
eboard[idx] = 0
idx += 1
if idx>size*size then exit end if
if eboard[idx]=0 then
eboard[idx] = 1
exit
end if
eboard[last] = 1
last += 1
end while
if idx=0 then exit end if
end while
end procedure
function idx_from_edge(integer x,y)
-- convert {x,y}, where one but not both of x,y are either 0
-- or s1, and the other is strictly 1..size, into 1..4*size.
-- if x=0 then x = 0 -- (logically, but obvs. pointless)
if x=s1 then x = size
elsif y=0 then y = size*2
elsif y=s1 then y = size*3
elsif x!=0 then ?9/0 end if -- not an edge?!
return x+y
end function
function edge_from_idx(integer xy)
-- convert 1..4*size into {0,1..size}/{s1,1..size}/{1..size,0}/{1..size,s1}
sequence res
integer c = floor((xy-1)/size)
switch c do
case 0: res = {0,xy}
case 1: res = {s1,xy-size}
case 2: res = {xy-size*2,0}
case 3: res = {xy-size*3,s1}
default: ?9/0
end switch
return res
end function
-- this is currently inlined, in case you were looking for it:
--procedure idx_from_x_y(integer x, y)
-- convert {1,1}..{size,size} to 1..size*size, for flat indexing
-- return (y-1)*size+x
--end function
function x_y_from_idx(integer idx)
-- convert 1..size*size to {1,1}..{size,size}
-- (absence of floor() on /size is a deliberate sanity check)
integer x = remainder(idx-1,size)+1,
y = (idx-x)/size + 1
return {x,y}
end function
function next_hint()
sequence edges = repeat(0,size*4)
integer x,y,r
for i=1 to length(results) do
{x,y,r} = results[i]
for j=1 to 1+(r==1) do
integer idx = idx_from_edge(x,y)
if edges[idx] then ?9/0 end if
edges[idx] = 1
{?,?,?,x,y} = results[i]
end for
end for
integer new_hinted = find(0,edges,hinted+1)
return new_hinted
end function
procedure explore_hints(integer new_hinted)
if new_hinted then
-- originally, it proved better to scan these backwards...
-- it now breaks (wrong tiles, I guess) if not flipped...
new_hinted = size*4+1-new_hinted
integer {x,y} = edge_from_idx(new_hinted), k
sequence rxy = {}, counts = {}
for i=1 to possible do
sequence p = probe(x,y,unpack(possibles[i]),false)
k = find(p,rxy)
if k=0 then
rxy = append(rxy,p)
counts = append(counts,1)
else
counts[k] += 1
end if
end for
k = max(counts)
if hinted=0
or minmaxcount=0
or k<minmaxcount then
minmaxcount = k
k = maxsq(counts,true)
minmaxmove = rxy[k]
minmaxmove[3] = -2
end if
new_hinted = size*4+1-new_hinted -- unflip
hinted = new_hinted
else
hinted = size*4
end if
end procedure
procedure find_common()
sequence all = repeat(1,size*size),
none = repeat(0,size*size)
for i=1 to possible do
all = sq_and(all,unpack(possibles[i]))
if all==none then exit end if
end for
knowns = {}
for i=1 to length(all) do
if all[i] then
knowns = append(knowns,x_y_from_idx(i))
end if
end for
end procedure
include pGUI.e
Ihandle dlg, game_canvas, gridsize, atoms, score, hints, debug,
progress, declare
constant colour_table = {CD_RED,
CD_LIGHT_GREEN,
CD_YELLOW,
CD_BLUE,
CD_ORANGE,
CD_PURPLE,
CD_CYAN,
CD_MAGENTA,
CD_GREEN,
CD_DARK_GREEN,
#bfef45, -- Lime
#fabebe, -- Pink
#469990, -- Teal
#e6beff, -- Lavender
#9A6324, -- Brown
#fffac8, -- Beige
#800000, -- Maroon
#aaffc3, -- Mint
#808000, -- Olive
#ffd8b1, -- Apricot
#000075} -- Navy
function colour(integer c)
c = mod(c-1,length(colour_table))+1
return colour_table[c]
end function
constant CD_HINTS = CD_DARK_GREY, -- (where to fire probe)
CD_MAYBE = CD_YELLOW, -- (probably an atom [scan not yet finished])
CD_KNOWN = CD_CYAN -- (known atoms [scan finished])
procedure redraw()
IupUpdate(game_canvas)
end procedure
function idle_action()
integer new_hinted = 0
if possible<length(possibles) then
trim_possibles()
hinted = 0
elsif tried<maxtry and length(possibles)<100_000 then
enumerate()
hinted = 0
elsif IupGetInt(hints,"VALUE")
and hinted<size*4 then
if possible>1
and hinted<size*4 then
new_hinted = next_hint()
explore_hints(new_hinted)
redraw()
end if
if possible=1
or hinted=size*4 then
hinted = size*4
find_common()
redraw()
end if
else
return IUP_IGNORE -- (disables idle)
end if
string title = sprintf("%,d / %,d (%d%%)",{possible,tried,100*(tried/maxtry)})
if new_hinted then
title &= sprintf(", move %d/%d",{new_hinted,size*4})
end if
IupSetStrAttribute(progress,"TITLE",title)
return IUP_DEFAULT
end function
constant idle_action_cb = Icallback("idle_action")
procedure start_idle()
IupSetAttribute(progress,"TITLE","-")
IupSetGlobalFunction("IDLE_ACTION",idle_action_cb)
end procedure
procedure new_game()
size = IupGetInt(gridsize,"VALUE")
s1 = size+1
s2 = size+2
count = IupGetInt(atoms,"VALUE")
while true do -- in case count too big
mask = #02
integer bits = 1
while mask<=size*size-count+1 do mask*=2 bits+=1 end while
--
-- Prevent overflow: must be able to store count*bits in a Phix atom.
-- count limits are therefore 13 on 5x5, 7 on 10x10, and 5 on 20x20,
-- on 32-bit, but 64-bit does 16 on 5x5, 9 on 10x10, and 7 on 20x20.
-- Many if not all of the silly-sized games this prohibits could not
-- possibly be fully analysed within a typical human lifespan anyway.
-- Besides just 5 atoms allows ambiguous/therefore unplayable games.
-- See also the comments before unpack() above. Trying to store too
-- many bits would trigger the sanity checks in pack()/unpack().
--
integer mb = iff(machine_bits()=32?53:64),
maxcount = min(floor(mb/bits),size*size)
if count<=maxcount then exit end if
count = maxcount
IupSetInt(atoms,"VALUE",count)
end while
eboard = repeat(0,size*size)
eboard[1..count] = 1
tried = 0
maxtry = choose(size*size,count)
possibles = {}
possible = 0
results = {}
guessxy = {}
guessclr = {}
hidden = {}
knowns = {}
minmaxcount = 0
gameboard = repeat(0,size*size)
bool active = IupGetInt(debug,"VALUE")
integer done = 0, x, y, xy
while done<count do
x = rand(size)
y = rand(size)
xy = (y-1)*size+x
if gameboard[xy]=0 then
gameboard[xy] = 1
hidden = append(hidden,{x,y})
done += 1
elsif not find(0,gameboard) then
?9/0 -- let's not loop forever!
-- (should now be prevented by maxcount above)
end if
end while
IupSetInt(declare, "ACTIVE", active)
if active then
guessxy = hidden
guessclr = repeat(CD_BLUE,length(guessxy))
end if
hints_used = (IupGetInt(hints,"VALUE") and not active)
start_idle()
end procedure
-- saved in redraw_cb(), for click testing in button_cb():
integer wh, -- width and height
mx, my -- margins
-- saved in declare_cb(), for adding to the score (10 each)
integer wrong = 0
function redraw_cb(Ihandle ih, integer /*posx*/, integer /*posy*/)
integer {w,h} = IupGetIntInt(ih, "DRAWSIZE")
-- calc width/height and margins (saved for button_cb):
wh = min(floor((w-10)/s2),floor((h-10)/s2))
mx = floor((w-wh*(s2))/2)
my = floor((h-wh*(s2))/2)
cdCanvas cddbuffer = IupGetAttributePtr(ih,"DBUFFER")
IupGLMakeCurrent(ih)
cdCanvasActivate(cddbuffer)
cdCanvasClear(cddbuffer)
-- outer edges (using one huge '+' shape)
cdCanvasSetForeground(cddbuffer,CD_GREY)
cdCanvasBox(cddbuffer,mx+wh,mx+wh*s1,my,my+wh*s2)
cdCanvasBox(cddbuffer,mx,mx+wh*s2,my+wh,my+wh*s1)
-- the inner size*size board (square)
cdCanvasSetForeground(cddbuffer,CD_LIGHT_GREY)
cdCanvasBox(cddbuffer,mx+wh,mx+wh*s1,my+wh,my+wh*s1)
-- draw the grid lines
cdCanvasSetForeground(cddbuffer,CD_WHITE)
integer {lx,ly} = {mx,my}
for i=1 to size+1 do
lx += wh
ly += wh
cdCanvasLine(cddbuffer,lx,my,lx,my+wh*s2)
cdCanvasLine(cddbuffer,mx,ly,mx+wh*s2,ly)
end for
sequence edges = repeat(0,size*4)
integer x,y,c = 1, h2 = floor(wh/2), r,
rfrom = (minmaxcount==0 or IupGetInt(hints,"VALUE")=0)
for i=rfrom to length(results) do
{x,y,r} = iff(i=0?minmaxmove:results[i])
integer cb, ct
string txt
{txt,cb,ct} = iff(r=-2?{"+",CD_HINTS,CD_BLACK}:
iff(r=-1?{"R",CD_WHITE,CD_BLACK}:
iff(r==0?{"H",CD_BLACK,CD_WHITE}:
{sprintf("%d",c),CD_GREY,colour(c)})))
for j=1 to 1+(r==1) do
cdCanvasSetForeground(cddbuffer,cb)
integer cx = mx+wh*x,
cy = my+wh*(s1-y)
cdCanvasBox(cddbuffer,cx+1,cx+wh,cy+1,cy+wh)
cdCanvasSetForeground(cddbuffer,ct)
cdCanvasFont(cddbuffer, "Helvetica", CD_BOLD, h2)
cdCanvasText(cddbuffer, cx+h2, cy+h2, txt)
if i!=0 then
integer idx = idx_from_edge(x,y)
if edges[idx] then ?9/0 end if
edges[idx] = 1
if r=1 then
{?,?,?,x,y} = results[i]
c += (j=2)
end if
end if
end for
end for
sequence gxy = guessxy,
gclr = guessclr
if IupGetInt(hints,"VALUE") then
for i=1 to length(knowns) do
sequence ki = knowns[i]
if not find(ki,gxy) then
gxy = append(gxy,ki)
gclr = append(gclr,iff(tried<maxtry?CD_MAYBE:CD_KNOWN))
end if
end for
end if
for i=1 to length(gxy) do
{x,y} = gxy[i]
atom cx = mx+(x+0.5)*wh,
cy = my+(s1-y+0.5)*wh
r = floor(wh*4/5)
cdCanvasSetForeground(cddbuffer,gclr[i])
cdCanvasCircle(cddbuffer, cx, cy, r)
end for
cdCanvasFlush(cddbuffer)
-- IupSetStrAttribute(score,"TITLE","%d",{iff(hints_used?9999:sum(edges)+wrong*10)})
IupSetStrAttribute(score,"TITLE","%d",{sum(edges)+wrong*10})
return IUP_DEFAULT
end function
function map_cb(Ihandle ih)
IupGLMakeCurrent(ih)
atom res = IupGetDouble(NULL, "SCREENDPI")/25.4
cdCanvas cddbuffer = cdCreateCanvas(CD_GL, "10x10 %g", {res})
IupSetAttributePtr(ih,"DBUFFER",cddbuffer)
cdCanvasSetBackground(cddbuffer, CD_PARCHMENT)
{} = cdCanvasTextAlignment(cddbuffer, CD_CENTER)
return IUP_DEFAULT
end function
function canvas_resize_cb(Ihandle canvas)
cdCanvas cddbuffer = IupGetAttributePtr(canvas,"DBUFFER")
integer {canvas_width, canvas_height} = IupGetIntInt(canvas, "DRAWSIZE")
atom res = IupGetDouble(NULL, "SCREENDPI")/25.4
cdCanvasSetAttribute(cddbuffer, "SIZE", "%dx%d %g", {canvas_width, canvas_height, res})
return IUP_DEFAULT
end function
function declare_cb(Ihandle /*declare*/)
sequence add_h = repeat(true,length(hidden))
wrong = max(0,count-length(guessxy))
for i=1 to length(guessxy) do
integer k = find(guessxy[i],hidden)
if k then
guessclr[i] = CD_GREEN
add_h[k] = false
else
guessclr[i] = CD_RED
wrong += 1
end if
end for
for i=1 to length(add_h) do
if add_h[i] then
guessxy = append(guessxy,hidden[i])
guessclr = append(guessclr,CD_BLUE)
end if
end for
IupSetAttribute(declare, "ACTIVE", "NO")
redraw()
return IUP_DEFAULT
end function
function button_cb(Ihandle canvas, integer button, pressed, x, y, atom /*pStatus*/)
Ihandle frame = IupGetParent(canvas)
string title = IupGetAttribute(frame,"TITLE")
if button=IUP_BUTTON1 and not pressed then -- (left button released)
x = floor((x-mx)/wh)
y = floor((y-my)/wh)
-- obviously, an x/y of 0 means left/top,
-- whereas s1 means right/btm,
-- and 1..size(both) is inner.
bool outerx = (x>=0 and x<=s1),
outery = (y>=0 and y<=s1),
innerx = (x>=1 and x<=size),
innery = (y>=1 and y<=size)
if innerx and innery then
sequence guess = {x,y}
integer k = find(guess,guessxy)
if k then
guessxy[k..k] = {}
guessclr[k..k] = {}
else
guessxy = append(guessxy,guess)
guessclr = append(guessclr,CD_BLUE)
end if
bool bActive = (length(guessxy)==count)
IupSetInt(declare, "ACTIVE", bActive)
if IupGetInt(debug,"VALUE")
and length(guessxy)=count then
hidden = guessxy
gameboard = repeat(0,size*size)
for i=1 to count do
{x,y} = hidden[i]
integer xy = (y-1)*size+x
gameboard[xy] = 1
end for
results = {}
end if
redraw()
elsif (outerx and innery)
or (outery and innerx) then
sequence r = probe(x,y,gameboard)
if not find(r,results) then
results = append(results,r)
possible = 0
start_idle()
end if
redraw()
end if
end if
return IUP_CONTINUE
end function
function new_game_cb(Ihandle /*ih*/)
new_game()
redraw()
return IUP_DEFAULT
end function
function exit_cb(Ihandle /*ih*/)
return IUP_CLOSE
end function
function help_cb(Ihandln /*ih*/)
IupMessage(title,help_text)
return IUP_DEFAULT
end function
function key_cb(Ihandle /*dlg*/, atom c)
if c=K_ESC then return IUP_CLOSE end if
if c=K_F1 then return help_cb(NULL) end if
if c='?' then
-- an old diagnostic that I kept in...
for i=1 to min(5,length(possibles)) do
sequence s = unpack(possibles[i])
for j=1 to size do
?s[1..size]
s = s[size+1..$]
end for
puts(1,"\n")
end for
possible = 0
start_idle()
end if
return IUP_CONTINUE
end function
function valuechanged_cb(Ihandle ih)
if ih=hints then
hints_used = true
start_idle()
else
new_game()
end if
redraw()
return IUP_DEFAULT
end function
constant cb_valuechanged = Icallback("valuechanged_cb")
procedure main()
IupOpen()
gridsize = IupText("SPIN=Yes, SPINMIN=1, SPINMAX=20, VALUE=8, RASTERSIZE=34x")
atoms = IupText("SPIN=Yes, SPINMIN=1, SPINMAX=16, VALUE=4, RASTERSIZE=34x")
score = IupLabel("","EXPAND=HORIZONTAL, PADDING=5x4")
hints = IupToggle(" Show Hints?","VALUE=YES, RIGHTBUTTON=YES, PADDING=5x4")
debug = IupToggle("Debug Mode?","VALUE=NO, RIGHTBUTTON=YES, PADDING=5x4")
progress = IupLabel("-","EXPAND=HORIZONTAL, PADDING=5x4")
declare = IupButton("Declare",Icallback("declare_cb"),"PADDING=5x4, ACTIVE=NO")
game_canvas = IupGLCanvas("RASTERSIZE=400x400")
Ihandle newgame = IupButton("New Game",Icallback("new_game_cb"),"PADDING=5x4"),
help = IupButton("Help (F1)",Icallback("help_cb"),"PADDING=5x4"),
quit = IupButton("E&xit",Icallback("exit_cb"),"PADDING=5x4"),
vbox = IupVbox({IupHbox({IupLabel("Size","PADDING=5x4"),gridsize,
IupFill(),
IupLabel("Atoms","PADDING=5x4"),atoms}),
IupHbox({hints,IupFill(),debug}),
IupHbox({progress}),
IupHbox({IupLabel("Score","PADDING=5x4"),score}),
IupHbox({declare,newgame,help,quit})},"MARGIN=5x5"),
game_frame = IupFrame(IupHbox({game_canvas},"MARGIN=3x3"),"TITLE=Game"),
option_frame = IupFrame(vbox,"TITLE=Options"),
full = IupHbox({game_frame,option_frame})
IupSetCallbacks({gridsize,atoms,hints,debug}, {"VALUECHANGED_CB", cb_valuechanged})
IupSetCallbacks(game_canvas, {"ACTION", Icallback("redraw_cb"),
"MAP_CB", Icallback("map_cb"),
"RESIZE_CB", Icallback("canvas_resize_cb"),
"BUTTON_CB", Icallback("button_cb")})
dlg = IupDialog(IupHbox({full},"MARGIN=3x3"))
IupSetAttribute(dlg, "TITLE", title)
IupSetCallback(dlg, "K_ANY", Icallback("key_cb"))
IupSetAttributeHandle(dlg,"DEFAULTENTER", declare) --erm...??
new_game()
IupShowXY(dlg,IUP_CENTER,IUP_CENTER)
IupSetAttribute(dlg, "RASTERSIZE", NULL)
IupSetStrAttribute(dlg, "MINSIZE", IupGetAttribute(dlg,"RASTERSIZE"))
sequence fixsize = {score,progress}
for i=1 to length(fixsize) do
Ihandle fi = fixsize[i]
IupSetAttributes(fi, "RASTERSIZE=%s, EXPAND=NO", {IupGetAttribute(fi,"RASTERSIZE")})
end for
IupMainLoop()
IupClose()
end procedure
main()
Wren
import "random" for Random
import "./fmt" for Fmt
import "./ioutil" for Input
import "./str" for Str
var b = List.filled(100, null) // displayed board
var h = List.filled(100, null) // hidden atoms
var wikiGame = true // set to false for a 'random' game
var rand = Random.new()
var hideAtoms = Fn.new {
var placed = 0
while (placed < 4) {
var a = rand.int(11, 89) // 11 to 88 inclusive
var m = a % 10
if (m == 0 || m == 9 || h[a] == "T") continue
h[a] = "T"
placed = placed + 1
}
}
var initialize = Fn.new {
for (i in 0..99) {
b[i] = " "
h[i] = "F"
}
if (!wikiGame) {
hideAtoms.call()
} else {
h[32] = "T"
h[37] = "T"
h[64] = "T"
h[87] = "T"
}
System.print("""
=== BLACK BOX ===
H Hit (scores 1)
R Reflection (scores 1)
1-9, Detour (scores 2)
a-c Detour for 10-12 (scores 2)
G Guess (maximum 4)
Y Correct guess
N Incorrect guess (scores 5)
A Unguessed atom
Cells are numbered a0 to j9.
Corner cells do nothing.
Use edge cells to fire beam.
Use middle cells to add/delete a guess.
Game ends automatically after 4 guesses.
Enter q to abort game at any time.
""")
}
var drawGrid = Fn.new { |score, guesses|
System.print(" 0 1 2 3 4 5 6 7 8 9 ")
System.print()
System.print(" ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗")
Fmt.lprint("a $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s",
[b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9]])
System.print(" ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗")
Fmt.lprint ("b ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║",
[b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]])
System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣")
Fmt.lprint ("c ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║",
[b[20], b[21], b[22], b[23], b[24], b[25], b[26], b[27], b[28], b[29]])
System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣")
Fmt.lprint ("d ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║",
[b[30], b[31], b[32], b[33], b[34], b[35], b[36], b[37], b[38], b[39]])
System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣")
Fmt.lprint ("e ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║",
[b[40], b[41], b[42], b[43], b[44], b[45], b[46], b[47], b[48], b[49]])
System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣")
Fmt.lprint ("f ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║",
[b[50], b[51], b[52], b[53], b[54], b[55], b[56], b[57], b[58], b[59]])
System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣")
Fmt.lprint ("g ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║",
[b[60], b[61], b[62], b[63], b[64], b[65], b[66], b[67], b[68], b[69]])
System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣")
Fmt.lprint ("h ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║",
[b[70], b[71], b[72], b[73], b[74], b[75], b[76], b[77], b[78], b[79]])
System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣")
Fmt.lprint ("i ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║",
[b[80], b[81], b[82], b[83], b[84], b[85], b[86], b[87], b[88], b[89]])
System.print(" ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝")
Fmt.lprint ("j $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s",
[b[90], b[91], b[92], b[93], b[94], b[95], b[96], b[97], b[98], b[99]])
System.print(" ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝")
var status = (guesses != 4) ? "In play" : "Game over!"
System.print("\n Score = %(score)\tGuesses = %(guesses)\t Status = %(status)\n")
}
var atCorner = Fn.new { |ix| ix == 0 || ix == 9 || ix == 90 || ix == 99 }
var inRange = Fn.new { |ix| ix >= 1 && ix <= 98 && ix != 9 && ix != 90 }
var atTop = Fn.new { |ix| ix >= 1 && ix <= 8 }
var atBottom = Fn.new { |ix| ix >= 91 && ix <= 98 }
var atLeft = Fn.new { |ix| inRange.call(ix) && ix%10 == 0 }
var atRight = Fn.new { |ix| inRange.call(ix) && ix%10 == 9 }
var inMiddle = Fn.new { |ix|
return inRange.call(ix) && !atTop.call(ix) && !atBottom.call(ix) &&
!atLeft.call(ix) && !atRight.call(ix)
}
var nextCell = Fn.new {
var ix
while (true) {
var sq = Str.lower(Input.text(" Choose cell : ", 1))
if (sq.count == 1 && sq[0] == "q") {
Fiber.abort("program aborted")
}
if (sq.count != 2 || !"abcdefghij".contains(sq[0]) || !"0123456789".contains(sq[1])) {
continue
}
ix = (sq[0].bytes[0] - 97) * 10 + sq[1].bytes[0] - 48
if (atCorner.call(ix)) continue
break
}
System.print()
return ix
}
var finalScore = Fn.new { |score, guesses|
for (i in 11..88) {
var m = i % 10
if (m == 0 || m == 9) continue
if (b[i] == "G" && h[i] == "T") {
b[i] = "Y"
} else if (b[i] == "G" && h[i] == "F") {
b[i] = "N"
score = score + 5
} else if (b[i] == " " && h[i] == "T") {
b[i] = "A"
}
}
drawGrid.call(score, guesses)
}
var play = Fn.new {
var score = 0
var guesses = 0
var num = "0"
while (true) {
var outer = false
drawGrid.call(score, guesses)
var ix = nextCell.call()
if (!inMiddle.call(ix) && b[ix] != " ") continue // already processed
var inc
var def
if (atTop.call(ix)) {
inc = 10
def = 1
} else if (atBottom.call(ix)) {
inc = -10
def = 1
} else if (atLeft.call(ix)) {
inc = 1
def = 10
} else if (atRight.call(ix)) {
inc = -1
def = 10
} else {
if (b[ix] != "G") {
b[ix] = "G"
guesses = guesses + 1
if (guesses == 4) break
} else {
b[ix] = " "
guesses = guesses - 1
}
continue
}
var x = ix + inc
var first = true
while (inMiddle.call(x)) {
if (h[x] == "T" ) { // hit
b[ix] = "H"
score = score + 1
first = false
outer = true
break
}
if (first && (inMiddle.call(x+def) && h[x+def] == "T") ||
(inMiddle.call(x-def) && h[x-def] == "T")) { // reflection
b[ix] = "R"
score = score + 1
first = false
outer = true
break
}
first = false
var y = x + inc - def
if (inMiddle.call(y) && h[y] == "T") { // deflection
if (inc.abs == 1) {
inc = 10
def = 1
} else if (inc.abs == 10) {
inc = 1
def = 10
}
}
y = x + inc + def
if (inMiddle.call(y) && h[y] == "T") { // deflection or double deflection
if (inc.abs == 1) {
inc = -10
def = 1
} else if (inc.abs == 10) {
inc = -1
def = 10
}
}
x = x + inc
}
if (outer) continue
if (num != "9") {
num = String.fromByte(num.bytes[0] + 1)
} else {
num = "a"
}
if (b[ix] == " ") score = score + 1
b[ix] = num
if (inRange.call(x)) {
if (ix == x) {
b[ix] = "R"
} else {
if (b[x] == " ") score = score + 1
b[x] = num
}
}
}
drawGrid.call(score, guesses)
finalScore.call(score, guesses)
}
while (true) {
initialize.call()
play.call()
var yn = Str.lower(Input.option(" Play again y/n : ", "ynYN"))
if (yn == "n") return
}
- Output:
Sample game (wikiGame == true):
=== BLACK BOX === H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time. 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 0 Guesses = 0 Status = In play Choose cell : b0 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 2 Guesses = 0 Status = In play Choose cell : c0 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 0 Status = In play Choose cell : d7 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 1 Status = In play Choose cell : d4 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ G ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 2 Status = In play Choose cell : e3 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ G ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ G ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 3 Status = In play Choose cell : h2 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ G ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ G ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ G ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 4 Status = Game over! 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ A ║ ║ N ║ ║ ║ Y ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ N ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ A ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ N ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ A ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 19 Guesses = 4 Status = Game over! Play again y/n : n
zkl
const ATM="A", F="F", HIT="H", G="G", GN="N", R="R", BLNK=" ", GY="Y";
var
brd,hdn, // displayed board & hidden atoms
wikiGame = True; // set to False for a 'random' game
fcn initialize{
brd,hdn = List.createLong(100,BLNK), List.createLong(100,F);
if(not wikiGame) hideAtoms();
else hdn[32] = hdn[37] = hdn[64] = hdn[87] = ATM;
// else hdn[64] = hdn[66] = ATM; // Double deflection case
println(
#<<<"
=== BLACK BOX ===
H Hit (scores 1)
R Reflection (scores 1)
1-9, Detour (scores 2)
a-c Detour for 10-12 (scores 2)
G Guess (maximum 4)
Y Correct guess
N Incorrect guess (scores 5)
A Unguessed atom
Cells are numbered a0 to j9.
Corner cells do nothing.
Use edge cells to fire beam.
Use middle cells to add/delete a guess.
Game ends automatically after 4 guesses.
Enter q to abort game at any time.\n\n");
#<<<
}
fcn drawGrid(score, guesses){
var [const] vbs="\u2550\u2550\u2550\u256c", bt=(vbs.del(-3,3)),
be1=String(" %s",vbs*7,bt,"%s").fmt,
b1=be1("\u2554","\u2557"), e1=be1("\u255a","\u255d"),
be2=String(" %s", vbs*9, bt,"%s").fmt,
b2=be2("\u2554", "\u2557"), b3=be2("\u256c", "\u256c"),
e2=be2("\u255a", "\u255d"),
g1=String("%s%s ","\u2551 %s "*9).fmt, // a brd[0]=brd[90]=" "
g2=String("%s ","\u2551 %s "*11).del(-3,3).fmt; // b c d .. i
println(" 0 1 2 3 4 5 6 7 8 9 \n",b1);
grid,sep,n := g1, b2, -10;
foreach c in (["a".."i"]){
println(grid(c,brd[n+=10,10].xplode()));
println((c=="i") and e2 or sep);
grid,sep = g2,b3;
}
println(g1("j",brd[90,10].xplode()), "\n", e1);
status:=(guesses==4) and "Game over!" or "In play";
println("\n Score = ", score, "\tGuesses = ", guesses, "\t Status = ", status);
}
fcn hideAtoms{
n:=4; do{
a,m:=(11).random(89), a % 10; // 11 to 88 inclusive
if(m==0 or m==9 or hdn[a]==ATM) continue;
hdn[a]=ATM;
n-=1;
}while(n);
}
fcn nextCell{
while(True){
s,c,n,sz := ask(" Choose cell [a-j][0-9]: ").strip().toLower(), s[0,1], s[1,1], s.len();
if(sz==1 and c=="q") System.exit();
if(not (sz==2 and ("a"<=c<="j") and ("0"<=n<="9"))) continue;
ix:=10*(c.toAsc() - 97) + n; // row major, "a"-'a'
if(not atCorner(ix)){ println(); return(ix); }
}
}
fcn atCorner(ix){ ix==0 or ix==9 or ix==90 or ix==99 }
fcn inRange(ix) { (1<=ix<=98) and ix!=9 and ix!=90 }
fcn atTop(ix) { 1<=ix<= 8 }
fcn atBottom(ix){ 91<=ix<=98 }
fcn atLeft(ix) { inRange(ix) and ix%10 ==0 }
fcn atRight(ix) { inRange(ix) and ix%10 ==9 }
fcn inMiddle(ix){
inRange(ix) and not ( atTop(ix) or atBottom(ix) or atLeft(ix) or atRight(ix) )
}
fcn play{
score,guesses,num := 0,0, 0x30; // '0'
while(True){
drawGrid(score, guesses);
ix:=nextCell();
if(not inMiddle(ix) and brd[ix]!=BLNK) continue; // already processed
inc,def := 0,0;
if (atTop(ix)) inc,def = 10, 1;
else if(atBottom(ix)) inc,def = -10, 1;
else if(atLeft(ix)) inc,def = 1, 10;
else if(atRight(ix)) inc,def = -1, 10;
else{
if(brd[ix]!=G){
brd[ix]=G;
if( (guesses+=1) ==4) break(1); // you done
}else{ brd[ix]=BLNK; guesses-=1; }
continue;
}
x,first := ix + inc, True;
while(inMiddle(x)){
if(hdn[x]==ATM){ // hit
brd[ix]=HIT;
score+=1;
first=False;
continue(2);
}
if(first and (inMiddle(x + def) and hdn[x + def]==ATM) or
(inMiddle(x - def) and hdn[x - def]==ATM)){ // reflection
brd[ix]=R;
score+=1;
first=False;
continue(2);
}
first=False;
y:=x + inc - def;
if(inMiddle(y) and hdn[y]==ATM){ // deflection
switch(inc){
case( 1, -1){ inc, def = 10, 1 }
case(10, -10){ inc, def = 1,10 }
}
}
y=x + inc + def;
if(inMiddle(y) and hdn[y]==ATM){ // deflection or double deflection
switch(inc){
case( 1, -1){ inc, def = -10, 1 }
case(10, -10){ inc, def = -1, 10 }
}
}
x+=inc;
}// while inMiddle
if(brd[ix]==BLNK) score+=1;
if(num!=0x39) num+=1; else num=97; // '0' & 'a'
brd[ix]=num.toChar(); // right back at ya
if(inRange(x)){
if(ix==x) brd[ix]=R;
else{
if(brd[x]==BLNK) score+=1;
brd[x]=num.toChar();
}
}
}
drawGrid( score, guesses);
finalScore(score, guesses);
}
fcn finalScore(score, guesses){
println(hdn.toString(*));
foreach i in ([11..88]){
m:=i%10;
if(m==0 or m==9) continue;
if(brd[i]==G and hdn[i]==ATM) brd[i]=GY;
else if(brd[i]==G and hdn[i]==F){ brd[i]=GN; score+=5; }
else if(brd[i]==BLNK and hdn[i]==ATM) brd[i]=ATM;
}
drawGrid(score, guesses);
}
while(True){
initialize(); play();
while(True){
yn:=ask(" Play again y/n : ").strip().toLower();
if(yn=="n") break(2);
else if(yn=="y") break(1);
}
}
Showing [results of] most of the Wikipedia actions:
- Output:
=== BLACK BOX === H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time. 0 1 2 3 4 5 6 7 8 9 ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ Score = 0 Guesses = 0 Status = In play Choose cell [a-j][0-9]: g9 0 1 2 3 4 5 6 7 8 9 ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ H ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ Score = 1 Guesses = 0 Status = In play ... Choose cell [a-j][0-9]: f0 0 1 2 3 4 5 6 7 8 9 ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ a ║ ║ ║ 3 ║ ║ 1 ║ 3 ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ 2 ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ 4 ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ f ║ 5 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ H ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ 4 ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ i ║ ║ ║ ║ ║ ║ ║ G ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ 5 ║ R ║ H ║ R ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ Score = 14 Guesses = 1 Status = In play