Task: 15 puzzle game




Go: package main

import (
	"fmt"
	"math/rand"
	"strings"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())
	p := newPuzzle()
	p.play()
}

type board [16]cell
type cell uint8
type move uint8

const (
	up move = iota
	down
	right
	left
)

func randMove() move { return move(rand.Intn(4)) }

var solvedBoard = board{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}

func (b *board) String() string {
	var buf strings.Builder
	for i, c := range b {
		if c == 0 {
			buf.WriteString("  .")
		} else {
			_, _ = fmt.Fprintf(&buf, "%3d", c)
		}
		if i%4 == 3 {
			buf.WriteString("\n")
		}
	}
	return buf.String()
}

type puzzle struct {
	board board
	empty int 
	moves int
	quit  bool
}

func newPuzzle() *puzzle {
	p := &puzzle{
		board: solvedBoard,
		empty: 15,
	}
	
	p.shuffle(50)
	return p
}

func (p *puzzle) shuffle(moves int) {
	
	
	for i := 0; i < moves || p.board == solvedBoard; {
		if p.doMove(randMove()) {
			i++
		}
	}
}

func (p *puzzle) isValidMove(m move) (newIndex int, ok bool) {
	switch m {
	case up:
		return p.empty - 4, p.empty/4 > 0
	case down:
		return p.empty + 4, p.empty/4 < 3
	case right:
		return p.empty + 1, p.empty%4 < 3
	case left:
		return p.empty - 1, p.empty%4 > 0
	default:
		panic("not reached")
	}
}

func (p *puzzle) doMove(m move) bool {
	i := p.empty
	j, ok := p.isValidMove(m)
	if ok {
		p.board[i], p.board[j] = p.board[j], p.board[i]
		p.empty = j
		p.moves++
	}
	return ok
}

func (p *puzzle) play() {
	fmt.Printf("Starting board:")
	for p.board != solvedBoard && !p.quit {
		fmt.Printf("\n%v\n", &p.board)
		p.playOneMove()
	}
	if p.board == solvedBoard {
		fmt.Printf("You solved the puzzle in %d moves.\n", p.moves)
	}
}

func (p *puzzle) playOneMove() {
	for {
		fmt.Printf("Enter move #%d (U, D, L, R, or Q): ", p.moves+1)
		var s string
		if n, err := fmt.Scanln(&s); err != nil || n != 1 {
			continue
		}

		s = strings.TrimSpace(s)
		if s == "" {
			continue
		}

		var m move
		switch s[0] {
		case 'U', 'u':
			m = up
		case 'D', 'd':
			m = down
		case 'L', 'l':
			m = left
		case 'R', 'r':
			m = right
		case 'Q', 'q':
			fmt.Printf("Quiting after %d moves.\n", p.moves)
			p.quit = true
			return
		default:
			fmt.Println(`
Please enter "U", "D", "L", or "R" to move the empty cell
up, down, left, or right. You can also enter "Q" to quit.
Upper or lowercase is accepted and only the first non-blank
character is important (i.e. you may enter "up" if you like).
`)
			continue
		}

		if !p.doMove(m) {
			fmt.Println("That is not a valid move at the moment.")
			continue
		}

		return
	}
}






COBOL:          >>SOURCE FORMAT FREE


identification division.
program-id. fifteen.
environment division.
configuration section.
repository. function all intrinsic.
data division.
working-storage section.

01  r pic 9.
01  r-empty pic 9.
01  r-to pic 9.
01  r-from pic 9.

01  c pic 9.
01  c-empty pic 9.
01  c-to pic 9.
01  c-from pic 9.

01  display-table.
    03  display-row occurs 4.
        05  display-cell occurs 4 pic 99.

01  tile-number pic 99.
01  tile-flags pic x(16).

01  display-move value spaces.
    03  tile-id pic 99.

01  row-separator pic x(21) value all '.'.
01  column-separator pic x(3) value ' . '.

01  inversions pic 99.
01  current-tile pic 99.

01  winning-display pic x(32) value
        '01020304'
    &   '05060708'
    &   '09101112'
    &   '13141500'.

procedure division.
start-fifteen.
    display 'start fifteen puzzle'
    display '    enter a two-digit tile number and press <enter> to move'
    display '    press <enter> only to exit'

    
    perform initialize-table with test after until inversions = 0
    perform show-table
    accept display-move
    perform until display-move = spaces
        perform move-tile
        perform show-table
        move spaces to display-move
        accept display-move
    end-perform
    stop run
    .
initialize-table.
    compute tile-number = random(seconds-past-midnight) 
    move spaces to tile-flags
    move 0 to current-tile inversions
    perform varying r from 1 by 1 until r > 4
    after c from 1 by 1 until c > 4
        perform with test after
        until tile-flags(tile-number + 1:1) = space
            compute tile-number = random() * 100
            compute tile-number = mod(tile-number, 16)
        end-perform
        move 'x' to tile-flags(tile-number + 1:1)
        if tile-number > 0 and < current-tile
            add 1 to inversions
        end-if
        move tile-number to display-cell(r,c) current-tile
    end-perform
    compute inversions = mod(inversions,2)
    .
show-table.
    if display-table = winning-display
        display 'winning'
    end-if
    display space row-separator
    perform varying r from 1 by 1 until r > 4
        perform varying c from 1 by 1 until c > 4
            display column-separator with no advancing
            if display-cell(r,c) = 00
                display '  ' with no advancing
                move r to r-empty
                move c to c-empty
            else
                display display-cell(r,c) with no advancing
            end-if
        end-perform
        display column-separator
    end-perform
    display space row-separator
    .
move-tile.
    if not (tile-id numeric and tile-id >= 01 and <= 15)
        display 'invalid tile number'
        exit paragraph
    end-if

    
    perform varying r from 1 by 1 until r > 4
    after c from 1 by 1 until c > 4
        if display-cell(r,c) = tile-id
            exit perform
        end-if
    end-perform

    
    evaluate true
    when r = r-empty
        if c-empty < c
            
            perform varying c-to from c-empty by 1 until c-to > c
                compute c-from = c-to + 1
                move display-cell(r-empty,c-from) to display-cell(r-empty,c-to)
            end-perform
        else
           
           perform varying c-to from c-empty by -1 until c-to < c
               compute c-from = c-to - 1
               move display-cell(r-empty,c-from) to display-cell(r-empty,c-to)
           end-perform
       end-if
       move 00 to display-cell(r,c)
    when c = c-empty
        if r-empty < r
            
            perform varying r-to from r-empty by 1 until r-to > r
                compute r-from = r-to + 1
                move display-cell(r-from,c-empty) to display-cell(r-to,c-empty)
            end-perform
        else
            
            perform varying r-to from r-empty by -1 until r-to < r
                compute r-from = r-to - 1
                move display-cell(r-from,c-empty) to display-cell(r-to,c-empty)
            end-perform
        end-if
        move 00 to display-cell(r,c)
    when other
         display 'invalid move'
    end-evaluate
    .
end program fifteen.