Black box

Revision as of 22:26, 13 March 2020 by Thundergnat (talk | contribs) (alphabetize, minor clean-up)

Implement a version of the Black Box game beginners configuration: 4 Atoms in an 8 x 8 grid.

Black box is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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

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. <lang go>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())
   }

}</lang>

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

JavaScript

Play it here. <lang javascript> 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();

} </lang>

zkl

Translation of: Go

<lang 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(
  1. <<<"
   === 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");
  1. <<<

}

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);
  }

}</lang> 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