Greed: Difference between revisions
m →{{header|REXX}}: added wording in the REXX section header about an OS dependency. |
Thundergnat (talk | contribs) m syntax highlighting fixup automation |
||
(23 intermediate revisions by 10 users not shown) | |||
Line 2: | Line 2: | ||
This task is about making a clone of the game "GREED" by Matthew Day. |
This task is about making a clone of the game "GREED" by Matthew Day. |
||
This game is played on a grid of 79 column by 22 rows of random numbers from 1 to |
This game is played on a grid of '''79''' column by '''22''' rows of random numbers from '''1''' to '''9''' (inclusive). |
||
The player location is signified by the '''@''' symbol. |
|||
The object of Greed is to erase as much of the screen as possible by moving around (all 8 directions are allowed) in this grid. When you move in a direction, you erase N number of grid squares in that direction, N being the first number in that direction. Your score reflects the total number of squares eaten. |
|||
The object of Greed is to erase as much of the screen as possible by moving around (all 8 directions are allowed in this grid). |
|||
When you move in a direction, '''N''' number of grid squares are erased ("eaten") in that direction, '''N''' being the first number in that direction. |
|||
Your score reflects the total number of squares "eaten". |
|||
You may not make a move that places you off the grid or over a previously eaten square. |
You may not make a move that places you off the grid or over a previously eaten square. |
||
Line 11: | Line 17: | ||
[https://www.youtube.com/watch?v=XQHq6tdxylk&list=PLdvB7n7RN2UDkjHAWCmbQ8okmgSrjWcvE Video on YouTube] |
[https://www.youtube.com/watch?v=XQHq6tdxylk&list=PLdvB7n7RN2UDkjHAWCmbQ8okmgSrjWcvE Video on YouTube] |
||
<br><br> |
|||
=={{header|C++}}== |
=={{header|C++}}== |
||
Windows console version. |
Windows console version. |
||
[[File:newGreedCpp.png|200px|thumb|right]] |
[[File:newGreedCpp.png|200px|thumb|right]] |
||
< |
<syntaxhighlight lang="cpp"> |
||
#include <windows.h> |
#include <windows.h> |
||
#include <iostream> |
#include <iostream> |
||
Line 137: | Line 143: | ||
greed g; g.play(); return 0; |
greed g; g.play(); return 0; |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Factor}}== |
|||
This uses Factor's own user interface vocabularies. Use hjkl-bnyu (vi-keys) to move. |
|||
<syntaxhighlight lang="factor">USING: accessors arrays colors combinators |
|||
combinators.short-circuit fry grouping io io.styles kernel lexer |
|||
literals make math math.matrices math.parser math.vectors random |
|||
sequences strings ui ui.commands ui.gadgets.panes |
|||
ui.gadgets.status-bar ui.gadgets.worlds ui.gestures |
|||
ui.pens.solid ; |
|||
IN: rosetta-code.greed |
|||
<< |
|||
SYNTAX: RGB: scan-token 2 cut 2 cut [ hex> 255 /f ] tri@ 1 |
|||
<rgba> suffix! ; |
|||
>> |
|||
CONSTANT: cells-width 79 |
|||
CONSTANT: cells-height 22 |
|||
CONSTANT: size 24 |
|||
CONSTANT: bg-color RGB: 000000 |
|||
CONSTANT: player-format { |
|||
{ font-size $ size } |
|||
{ foreground RGB: 5990C8 } |
|||
{ background RGB: B96646 } |
|||
} |
|||
CONSTANT: normal-format { { font-size $ size } } |
|||
CONSTANT: colors { |
|||
RGB: 40B4A4 |
|||
RGB: 40B3B7 |
|||
RGB: 40A2B9 |
|||
RGB: 408FBC |
|||
RGB: 407CBF |
|||
RGB: 4268C0 |
|||
RGB: 4355C2 |
|||
RGB: 4845C3 |
|||
RGB: 5F46C4 |
|||
} |
|||
CONSTANT: neighbors { |
|||
{ -1 -1 } { -1 0 } { -1 1 } |
|||
{ 0 -1 } { 0 1 } |
|||
{ 1 -1 } { 1 0 } { 1 1 } |
|||
} |
|||
TUPLE: greed < pane cells x y score ; |
|||
: set-player ( greed elt -- ) |
|||
'[ y>> _ swap ] [ x>> 2array ] [ cells>> ] tri set-index ; |
|||
: place-player ( greed -- ) 0 set-player ; |
|||
: remove-player ( greed -- ) f set-player ; |
|||
: make-cells ( -- cells ) |
|||
cells-width cells-height * [ 9 random 1 + ] replicate |
|||
cells-width group ; |
|||
: write-number ( n/f -- ) |
|||
[ >digit 1string normal-format first foreground ] |
|||
[ 1 - colors nth 2array ] bi 2array format ; |
|||
: write-cell ( n/f -- ) |
|||
{ |
|||
{ f [ " " normal-format format ] } |
|||
{ 0 [ "@" player-format format ] } |
|||
[ write-number ] |
|||
} case ; |
|||
: write-cells ( cells -- ) [ [ write-cell ] each nl ] each ; |
|||
: update-cells ( greed -- ) |
|||
dup cells>> [ write-cells ] curry with-pane ; |
|||
: init-greed ( greed -- greed' ) |
|||
make-cells >>cells cells-width random >>x cells-height |
|||
random >>y 0 >>score dup place-player dup update-cells dup |
|||
"Score: 0" swap show-status ; |
|||
: <greed> ( -- greed ) |
|||
f greed new-pane bg-color <solid> >>interior init-greed ; |
|||
: ?r,c ( r c matrix -- elt/f ) swapd ?nth ?nth ; |
|||
: ?r,cths ( seq matrix -- newseq ) |
|||
[ [ first2 ] dip ?r,c ] curry map ; |
|||
: (ray) ( start-loc dir length -- seq ) |
|||
1 + [ [ [ v+ ] keep over , ] times ] { } make 2nip ; |
|||
: ray ( start-loc dir length -- seq/f ) |
|||
dup [ (ray) ] [ 2nip ] if ; |
|||
: ?r,c-dir ( r c dir matrix -- n ) |
|||
[ 2array ] [ v+ first2 ] [ ?r,c ] tri* ; |
|||
: move-length ( greed dir -- n ) |
|||
[ [ y>> ] [ x>> ] [ ] tri ] dip swap cells>> ?r,c-dir ; |
|||
: y,x>loc ( greed -- loc ) [ y>> ] [ x>> ] bi 2array ; |
|||
: ray-dir ( greed dir -- seq ) |
|||
[ [ y,x>loc ] dip ] [ move-length ] 2bi ray ; |
|||
: in-bounds? ( dim loc -- ? ) |
|||
{ [ nip [ 0 >= ] all? ] [ v- [ 0 > ] all? ] } 2&& ; |
|||
: endpoint-in-bounds? ( greed dir -- ? ) |
|||
ray-dir dup [ |
|||
last ${ cells-height cells-width } swap in-bounds? |
|||
] when ; |
|||
: gapless? ( greed dir -- ? ) |
|||
[ ray-dir ] [ drop cells>> ?r,cths ] 2bi [ integer? ] all? ; |
|||
: can-move? ( greed dir -- ? ) |
|||
{ [ endpoint-in-bounds? ] [ gapless? ] } 2&& ; |
|||
: can-move-any? ( greed -- ? ) |
|||
neighbors [ can-move? ] with map [ t = ] any? ; |
|||
: setup-move ( greed dir -- seq ) over remove-player ray-dir ; |
|||
: update-score ( greed dir -- greed dir ) |
|||
2dup move-length pick swap [ + ] curry change-score dup |
|||
score>> number>string "Score: " prepend swap show-status ; |
|||
: (move) ( greed dir -- ) |
|||
update-score [ drop f ] [ setup-move dup last ] |
|||
[ drop cells>> swap [ set-indices ] dip ] 2tri first2 |
|||
[ >>y ] dip >>x place-player ; |
|||
: game-over ( greed -- ) |
|||
[ |
|||
score>> number>string "Game over! Final score: " |
|||
prepend " Press <space> for new game." append |
|||
] [ show-status ] bi ; |
|||
: ?game-over ( greed -- ) |
|||
dup can-move-any? [ drop ] [ game-over ] if ; |
|||
: move ( greed dir -- ) |
|||
dupd 2dup can-move? [ (move) ] [ 2drop ] if |
|||
[ update-cells ] [ ?game-over ] bi ; |
|||
: ?new-game ( greed -- ) |
|||
dup can-move-any? [ drop ] [ init-greed drop ] if ; |
|||
: e ( greed -- ) { 0 1 } move ; |
|||
: se ( greed -- ) { 1 1 } move ; |
|||
: s ( greed -- ) { 1 0 } move ; |
|||
: sw ( greed -- ) { 1 -1 } move ; |
|||
: w ( greed -- ) { 0 -1 } move ; |
|||
: nw ( greed -- ) { -1 -1 } move ; |
|||
: n ( greed -- ) { -1 0 } move ; |
|||
: ne ( greed -- ) { -1 1 } move ; |
|||
greed "gestures" f { |
|||
{ T{ key-down { sym "l" } } e } |
|||
{ T{ key-down { sym "n" } } se } |
|||
{ T{ key-down { sym "j" } } s } |
|||
{ T{ key-down { sym "b" } } sw } |
|||
{ T{ key-down { sym "h" } } w } |
|||
{ T{ key-down { sym "y" } } nw } |
|||
{ T{ key-down { sym "k" } } n } |
|||
{ T{ key-down { sym "u" } } ne } |
|||
{ T{ key-down { sym " " } } ?new-game } |
|||
} define-command-map |
|||
: greed-window ( -- ) |
|||
[ |
|||
<greed> <world-attributes> "Greed" >>title |
|||
open-status-window |
|||
] with-ui ; |
|||
MAIN: greed-window</syntaxhighlight> |
|||
{{out}} |
|||
[https://i.imgur.com/3IEo8cC.png Screenshot of the game after a loss] |
|||
=={{header|Go}}== |
|||
{{trans|C++}} |
|||
{{libheader|termbox-go}} |
|||
{{works with|Ubuntu 16.04}} |
|||
<br> |
|||
This hasn't been tested on Windows 10 but should work. |
|||
Note that this version uses the Z key (rather than the Y key) to move diagonally downwards to the left. A leave key, L, has also been added in case one wants to end the game prematurely. |
|||
<syntaxhighlight lang="go">package main |
|||
import ( |
|||
"fmt" |
|||
"github.com/nsf/termbox-go" |
|||
"log" |
|||
"math/rand" |
|||
"strconv" |
|||
"time" |
|||
) |
|||
type coord struct{ x, y int } |
|||
const ( |
|||
width = 79 |
|||
height = 22 |
|||
nCount = float64(width * height) |
|||
) |
|||
var ( |
|||
board [width * height]int |
|||
score = 0 |
|||
bold = termbox.AttrBold |
|||
cursor coord |
|||
) |
|||
var colors = [10]termbox.Attribute{ |
|||
termbox.ColorDefault, |
|||
termbox.ColorWhite, |
|||
termbox.ColorBlack | bold, |
|||
termbox.ColorBlue | bold, |
|||
termbox.ColorGreen | bold, |
|||
termbox.ColorCyan | bold, |
|||
termbox.ColorRed | bold, |
|||
termbox.ColorMagenta | bold, |
|||
termbox.ColorYellow | bold, |
|||
termbox.ColorWhite | bold, |
|||
} |
|||
func printAt(x, y int, s string, fg, bg termbox.Attribute) { |
|||
for _, r := range s { |
|||
termbox.SetCell(x, y, r, fg, bg) |
|||
x++ |
|||
} |
|||
} |
|||
func createBoard() { |
|||
for y := 0; y < height; y++ { |
|||
for x := 0; x < width; x++ { |
|||
board[x+width*y] = rand.Intn(9) + 1 |
|||
} |
|||
} |
|||
cursor = coord{rand.Intn(width), rand.Intn(height)} |
|||
board[cursor.x+width*cursor.y] = 0 |
|||
score = 0 |
|||
printScore() |
|||
} |
|||
func displayBoard() { |
|||
termbox.SetCursor(0, 0) |
|||
bg := colors[0] |
|||
for y := 0; y < height; y++ { |
|||
for x := 0; x < width; x++ { |
|||
i := board[x+width*y] |
|||
fg := colors[i] |
|||
s := " " |
|||
if i > 0 { |
|||
s = strconv.Itoa(i) |
|||
} |
|||
printAt(x, y, s, fg, bg) |
|||
} |
|||
} |
|||
fg := colors[9] |
|||
termbox.SetCursor(cursor.x, cursor.y) |
|||
printAt(cursor.x, cursor.y, "@", fg, bg) |
|||
termbox.Flush() |
|||
} |
|||
func printScore() { |
|||
termbox.SetCursor(0, 24) |
|||
fg := colors[4] |
|||
bg := termbox.ColorGreen |
|||
s := fmt.Sprintf(" SCORE: %d : %.3f%% ", score, float64(score)*100.0/nCount) |
|||
printAt(0, 24, s, fg, bg) |
|||
termbox.Flush() |
|||
} |
|||
func execute(x, y int) { |
|||
i := board[cursor.x+x+width*(cursor.y+y)] |
|||
if countSteps(i, x, y) { |
|||
score += i |
|||
for i != 0 { |
|||
i-- |
|||
cursor.x += x |
|||
cursor.y += y |
|||
board[cursor.x+width*cursor.y] = 0 |
|||
} |
|||
} |
|||
} |
|||
func countSteps(i, x, y int) bool { |
|||
t := cursor |
|||
for i != 0 { |
|||
i-- |
|||
t.x += x |
|||
t.y += y |
|||
if t.x < 0 || t.y < 0 || t.x >= width || t.y >= height || board[t.x+width*t.y] == 0 { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
func existsMoves() bool { |
|||
for y := -1; y < 2; y++ { |
|||
for x := -1; x < 2; x++ { |
|||
if x == 0 && y == 0 { |
|||
continue |
|||
} |
|||
ix := cursor.x + x + width*(cursor.y+y) |
|||
i := 0 |
|||
if ix >= 0 && ix < len(board) { |
|||
i = board[ix] |
|||
} |
|||
if i > 0 && countSteps(i, x, y) { |
|||
return true |
|||
} |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
func check(err error) { |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
} |
|||
func main() { |
|||
rand.Seed(time.Now().UnixNano()) |
|||
err := termbox.Init() |
|||
check(err) |
|||
defer termbox.Close() |
|||
eventQueue := make(chan termbox.Event) |
|||
go func() { |
|||
for { |
|||
eventQueue <- termbox.PollEvent() |
|||
} |
|||
}() |
|||
for { |
|||
termbox.HideCursor() |
|||
createBoard() |
|||
for { |
|||
displayBoard() |
|||
select { |
|||
case ev := <-eventQueue: |
|||
if ev.Type == termbox.EventKey { |
|||
switch ev.Ch { |
|||
case 'q', 'Q': |
|||
if cursor.x > 0 && cursor.y > 0 { |
|||
execute(-1, -1) |
|||
} |
|||
case 'w', 'W': |
|||
if cursor.y > 0 { |
|||
execute(0, -1) |
|||
} |
|||
case 'e', 'E': |
|||
if cursor.x < width-1 && cursor.y > 0 { |
|||
execute(1, -1) |
|||
} |
|||
case 'a', 'A': |
|||
if cursor.x > 0 { |
|||
execute(-1, 0) |
|||
} |
|||
case 'd', 'D': |
|||
if cursor.x < width-1 { |
|||
execute(1, 0) |
|||
} |
|||
case 'z', 'Z': |
|||
if cursor.x > 0 && cursor.y < height-1 { |
|||
execute(-1, 1) |
|||
} |
|||
case 'x', 'X': |
|||
if cursor.y < height-1 { |
|||
execute(0, 1) |
|||
} |
|||
case 'c', 'C': |
|||
if cursor.x < width-1 && cursor.y < height-1 { |
|||
execute(1, 1) |
|||
} |
|||
case 'l', 'L': // leave key |
|||
return |
|||
} |
|||
} else if ev.Type == termbox.EventResize { |
|||
termbox.Flush() |
|||
} |
|||
} |
|||
printScore() |
|||
if !existsMoves() { |
|||
break |
|||
} |
|||
} |
|||
displayBoard() |
|||
fg := colors[7] |
|||
bg := colors[0] |
|||
printAt(19, 8, "+----------------------------------------+", fg, bg) |
|||
printAt(19, 9, "| GAME OVER |", fg, bg) |
|||
printAt(19, 10, "| PLAY AGAIN(Y/N)? |", fg, bg) |
|||
printAt(19, 11, "+----------------------------------------+", fg, bg) |
|||
termbox.SetCursor(48, 10) |
|||
termbox.Flush() |
|||
select { |
|||
case ev := <-eventQueue: |
|||
if ev.Type == termbox.EventKey { |
|||
if ev.Ch == 'y' || ev.Ch == 'Y' { |
|||
break |
|||
} else { |
|||
return |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
Line 143: | Line 563: | ||
{{works with|Windows 10}} |
{{works with|Windows 10}} |
||
Note that this version uses the Z key (rather than the Y key) to move diagonally downwards to the left. |
Note that this version uses the Z key (rather than the Y key) to move diagonally downwards to the left. |
||
< |
<syntaxhighlight lang="scala">// Kotlin Native v0.5 |
||
import kotlinx.cinterop.* |
import kotlinx.cinterop.* |
||
Line 340: | Line 760: | ||
srand(time(null).toInt()) |
srand(time(null).toInt()) |
||
Greed().play() |
Greed().play() |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Java}}== |
=={{header|Java}}== |
||
Line 346: | Line 766: | ||
See [[Greed/Java]]. |
See [[Greed/Java]]. |
||
<br><br> |
<br><br> |
||
=={{header|Julia}}== |
|||
GUI version. Click a square adjacent to the "@" symbol to move. |
|||
<syntaxhighlight lang="julia">using Gtk |
|||
struct BState |
|||
board::Matrix{Int} |
|||
row::Int |
|||
col::Int |
|||
end |
|||
function greedapp(r, c) |
|||
rows, cols = c, r # gtk rotates grid 90 degrees |
|||
win = GtkWindow("Greed Game", 1200, 400) |> (GtkFrame() |> (box = GtkBox(:v))) |
|||
toolbar = GtkToolbar() |
|||
newgame = GtkToolButton("New Game") |
|||
set_gtk_property!(newgame, :label, "New Game") |
|||
set_gtk_property!(newgame, :is_important, true) |
|||
undomove = GtkToolButton("Undo Move") |
|||
set_gtk_property!(undomove, :label, "Undo Move") |
|||
set_gtk_property!(undomove, :is_important, true) |
|||
map(w->push!(toolbar,w),[newgame,undomove]) |
|||
scrwin = GtkScrolledWindow() |
|||
grid = GtkGrid() |
|||
map(w -> push!(box, w),[toolbar, scrwin]) |
|||
push!(scrwin, grid) |
|||
buttons = Array{Gtk.GtkButtonLeaf, 2}(undef, rows, cols) |
|||
for i in 1:rows, j in 1:cols |
|||
grid[i,j] = buttons[i,j] = GtkButton() |
|||
set_gtk_property!(buttons[i,j], :expand, true) |
|||
end |
|||
function findrowcol(button) |
|||
for i in 1:rows, j in 1:cols |
|||
if buttons[i, j] == button |
|||
return i, j |
|||
end |
|||
end |
|||
return 0, 0 |
|||
end |
|||
board = zeros(Int, rows, cols) |
|||
pastboardstates = Vector{BState}() |
|||
score = 0 |
|||
condition = Condition() |
|||
won = "" |
|||
myrow, mycol = 1, 1 |
|||
function update!() |
|||
for i in 1:rows, j in 1:cols |
|||
label = (board[i, j] > 0) ? board[i, j] : " " |
|||
set_gtk_property!(buttons[i, j], :label, label) |
|||
end |
|||
set_gtk_property!(buttons[myrow, mycol], :label, "@") |
|||
won = all(iszero, board) ? "WINNING" : "" |
|||
set_gtk_property!(win, :title, "$won Greed Game (Score: $score)") |
|||
end |
|||
function erasefromtile!(moverow, movecol) |
|||
xdir, ydir = moverow - myrow, movecol - mycol |
|||
if abs(xdir) > 1 || abs(ydir) > 1 || 0 == xdir == ydir || board[moverow, movecol] == 0 |
|||
return |
|||
end |
|||
push!(pastboardstates, BState(deepcopy(board), myrow, mycol)) |
|||
for i in 1:board[moverow, movecol] |
|||
x, y = myrow + xdir * i, mycol + ydir * i |
|||
if 0 < x <= rows && 0 < y <= cols |
|||
board[x, y] = 0 |
|||
score += 1 |
|||
end |
|||
end |
|||
board[myrow, mycol] = 0 |
|||
myrow = moverow |
|||
mycol = movecol |
|||
update!() |
|||
end |
|||
clicked(button) = begin x, y = findrowcol(button); erasefromtile!(x, y) end |
|||
function initialize!(w) |
|||
won = "" |
|||
possiblevals = collect(1:9) |
|||
for i in 1:rows, j in 1:cols |
|||
board[i, j] = rand(possiblevals) |
|||
set_gtk_property!(buttons[i,j], :label, board[i, j]) |
|||
signal_connect(clicked, buttons[i, j], "clicked") |
|||
end |
|||
myrow = rand(1:rows) |
|||
mycol = rand(1:cols) |
|||
board[myrow, mycol] = 0 |
|||
update!() |
|||
end |
|||
function undo!(w) |
|||
if won == "" && length(pastboardstates) > 0 |
|||
bst = pop!(pastboardstates) |
|||
board, myrow, mycol = bst.board, bst.row, bst.col |
|||
update!() |
|||
end |
|||
end |
|||
endit(w) = notify(condition) |
|||
initialize!(win) |
|||
signal_connect(initialize!, newgame, :clicked) |
|||
signal_connect(undo!, undomove, :clicked) |
|||
signal_connect(endit, win, :destroy) |
|||
showall(win) |
|||
wait(condition) |
|||
end |
|||
# greedapp(22, 79) # This would be per task, though a smaller game board is nicer |
|||
greedapp(12, 29) |
|||
</syntaxhighlight> |
|||
=={{header|Nim}}== |
|||
{{trans|Julia}} |
|||
{{libheader|gintro}} |
|||
This is a translation of the Julia solution with some modifications. The most important is the way the new position is computed after choosing a direction. Rather than moving to the neighbor tile, we move to the farthest tile in the chosen direction as shown in the video. |
|||
There is of course a lot of differences due to the way <code>gintro</code> library works which is quite different of Julia GTK API. |
|||
And we also added a score field to the BState object in order to restore the correct score when undoing a move. |
|||
<syntaxhighlight lang="nim">import random, strutils |
|||
import gintro/[gobject, glib, gtk, gio] |
|||
const Rows = 22 |
|||
const Cols = 79 |
|||
type |
|||
Board = array[Rows, array[Cols, int]] |
|||
BState = object |
|||
board: Board |
|||
score: Natural |
|||
row, col: int |
|||
Game = ref object |
|||
window: Window |
|||
score: Natural |
|||
won: string |
|||
board: Board |
|||
buttons: array[Rows, array[Cols, Button]] |
|||
myRow, myCol: Natural |
|||
pastBoardStates: seq[BState] |
|||
proc update(game: Game) = |
|||
## Update game state. |
|||
var won = true |
|||
for i in 0..<Rows: |
|||
for j in 0..<Cols: |
|||
let val = game.board[i][j] |
|||
won = won and val == 0 |
|||
game.buttons[i][j].setLabel(if val > 0: $val else: "") |
|||
game.buttons[game.myRow][game.myCol].setLabel("@") |
|||
game.won = if won: "WINNING " else: "" |
|||
game.window.setTitle(game.won & "Greed game (score: $#)".format(game.score)) |
|||
proc eraseFromTile(game: Game; moveRow, moveCol: Natural) = |
|||
## Erase tile values from the given tile. |
|||
let xdir = moveRow - game.myRow |
|||
let ydir = moveCol - game.myCol |
|||
if abs(xdir) > 1 or abs(ydir) > 1 or xdir == 0 and ydir == 0 or game.board[moveRow][moveCol] == 0: |
|||
return |
|||
game.pastBoardStates.add BState(board: game.board, score: game.score, |
|||
row: game.myRow, col: game.myCol) |
|||
let count = game.board[moveRow][moveCol] |
|||
var x: int = game.myRow |
|||
var y: int = game.myCol |
|||
for i in 1..count: |
|||
inc x, xdir |
|||
inc y, ydir |
|||
if x in 0..<Rows and y in 0..<Cols: |
|||
game.board[x][y] = 0 |
|||
inc game.score |
|||
else: |
|||
# Restore to last valid position. |
|||
dec x, xdir |
|||
dec y, ydir |
|||
game.board[game.myRow][game.myCol] = 0 |
|||
game.myRow = x |
|||
game.myCol = y |
|||
game.update() |
|||
proc clicked(b: Button; game: Game) = |
|||
## Callback tp process a click in the grid. |
|||
var row, col = -1 |
|||
for i in 0..<Rows: |
|||
for j in 0..<Cols: |
|||
if game.buttons[i][j] == b: |
|||
(row, col) = (i, j) |
|||
break |
|||
if row < 0: return |
|||
game.eraseFromTile(row, col) |
|||
proc undo(b: ToolButton; game: Game) = |
|||
## Callback to undo last move. |
|||
if game.won.len == 0 and game.pastBoardStates.len > 0: |
|||
let bst = game.pastBoardStates.pop() |
|||
game.board = bst.board |
|||
game.score = bst.score |
|||
game.myRow = bst.row |
|||
game.myCol = bst.col |
|||
game.update() |
|||
proc initialize(game: Game) = |
|||
## Initialize the game. |
|||
const PossibleVals = {1..9} |
|||
game.score = 0 |
|||
game.won = "" |
|||
for i in 0..<Rows: |
|||
for j in 0..<Cols: |
|||
game.board[i][j] = PossibleVals.sample() |
|||
game.buttons[i][j].connect("clicked", clicked, game) |
|||
game.myRow = rand(Rows - 1) |
|||
game.myCol = rand(Cols - 1) |
|||
game.update() |
|||
proc initGame(b: ToolButton; game: Game) = |
|||
## Callback to start a new game. |
|||
game.initialize() |
|||
proc activate(app: Application) = |
|||
## Activate the application. |
|||
let game = new(Game) |
|||
let window = app.newApplicationWindow() |
|||
window.setSizeRequest(1600, 800) |
|||
window.setTitle("Greed game") |
|||
game.window = window |
|||
let box = newBox(Orientation.vertical, 0) |
|||
window.add box |
|||
let toolbar = newToolbar() |
|||
box.add toolbar |
|||
let newGame = newToolButton(label = "New game") |
|||
let undoMove = newToolButton(label = "Undo move") |
|||
toolbar.add(newGame) |
|||
toolbar.add(undoMove) |
|||
let srcWindow = newScrolledWindow() |
|||
box.packEnd(srcWindow, true, true, 0) |
|||
let grid = newGrid() |
|||
srcWindow.add grid |
|||
for i in 0..<Rows: |
|||
for j in 0..<Cols: |
|||
let b = newButton() |
|||
game.buttons[i][j] = b |
|||
grid.attach(b, j, i, 1, 1) |
|||
game.initialize() |
|||
newGame.connect("clicked", initGame, game) |
|||
undoMove.connect("clicked", undo, game) |
|||
game.window.showAll() |
|||
randomize() |
|||
let app = newApplication(Application, "Rosetta.greed") |
|||
discard app.connect("activate", activate) |
|||
discard app.run()</syntaxhighlight> |
|||
=={{header|Perl}}== |
|||
{{trans|Raku}} |
|||
<syntaxhighlight lang="perl">use strict; |
|||
use warnings; |
|||
use feature 'say'; |
|||
my @board; |
|||
my $w = 79; |
|||
my $h = 22; |
|||
for (1..$h) { |
|||
my @row; |
|||
push @row, int 1 + rand 9 for 1..$w; |
|||
push @board, [@row]; |
|||
} |
|||
my $X = int 0.5 + rand $w; |
|||
my $Y = int 0.5 + rand $h; |
|||
$board[$Y][$X] = '@'; |
|||
my $score = 0; |
|||
sub execute { |
|||
my($y,$x) = @_; |
|||
my $i = $board[$Y+$y][$X+$x]; |
|||
if (countSteps($i, $x, $y)) { |
|||
$score += $i; |
|||
$board[ $Y + $y*$_ ][ $X + $x*$_ ] = ' ' for 0..$i; |
|||
$board[ $Y += $y*$i ][ $X += $x*$i ] = '@'; |
|||
} |
|||
} |
|||
sub countSteps { |
|||
my($i, $x, $y) = @_; |
|||
my $tX = $X; |
|||
my $tY = $Y; |
|||
for (0..$i) { |
|||
$tX += $x; |
|||
$tY += $y; |
|||
return 0 if $tX < 0 or $tY < 0 or $tX >= $w or $tY >= $h or $board[$tY][$tX] eq ' ' |
|||
} |
|||
return 1 |
|||
} |
|||
sub existsMoves { |
|||
for ([-1,-1], [-1,0], [-1,1], [0,-1], [0,0], [0,1], [1,-1], [1,0], [1,1]) { |
|||
my($x,$y) = @$_; |
|||
next if $x == 0 and $y == 0; |
|||
next if $X+$x < 0 or $X+$x > $w or $Y+$y < 0 or $Y+$y > $h ; |
|||
my $i = $board[$Y+$y][$X+$x]; |
|||
return 1 if ( $i ne ' ' and countSteps($i, $x, $y) ) |
|||
} |
|||
return 0; |
|||
} |
|||
while () { |
|||
say join '', @$_ for @board; |
|||
say "Game over." and last unless existsMoves(); |
|||
print "Current score : " . $score . "\n"; |
|||
my $c = <> ; chomp $c; |
|||
if ($c eq 'q') { say "So long." and last} |
|||
if ($c eq 'e') { execute(-1,-1) if $X > 0 and $Y > 0 } # North-West |
|||
if ($c eq 'r') { execute(-1, 0) if $Y > 0 } # North |
|||
if ($c eq 't') { execute(-1, 1) if $X < $w and $Y > 0 } # North-East |
|||
if ($c eq 'd') { execute( 0,-1) if $X > 0 } # West |
|||
if ($c eq 'g') { execute( 0, 1) if $X < $w } # East |
|||
if ($c eq 'x') { execute( 1,-1) if $X > 0 and $Y < $h } # South-West |
|||
if ($c eq 'c') { execute( 1, 0) if $Y < $h } # South |
|||
if ($c eq 'v') { execute( 1, 1) if $X < $w and $Y < $h } # South-East |
|||
}</syntaxhighlight> |
|||
=={{header|Phix}}== |
|||
{{trans|C++}} |
|||
<syntaxhighlight lang="phix">constant W = 79, H = 22, NCOUNT = W*H |
|||
sequence board |
|||
integer X, Y, score |
|||
procedure printScore() |
|||
position(25,1); bk_color(2); text_color(10) |
|||
printf(1," SCORE: %d : %f%% ",{score,score*100/NCOUNT}); |
|||
end procedure |
|||
procedure createBoard() |
|||
board = repeat(repeat('0',W),H) |
|||
for y=1 to H do |
|||
for x=1 to W do |
|||
board[y,x] = '0'+rand(9) |
|||
end for |
|||
end for |
|||
X = rand(W); Y = rand(H); |
|||
board[Y,X] = '0'; score = 0; |
|||
printScore(); |
|||
end procedure |
|||
procedure displayBoard() |
|||
position(1,1) |
|||
bk_color(2) |
|||
for y=1 to H do |
|||
for x=1 to W do |
|||
integer ch = board[y,x]; |
|||
text_color(iff(ch=' '?6:6+ch-'0')) |
|||
puts(1,ch) |
|||
end for |
|||
puts(1,"\n") |
|||
end for |
|||
bk_color(4); text_color(15); position(Y,X); puts(1,"@") |
|||
end procedure |
|||
function countSteps(integer i, x, y) |
|||
integer tX = X, tY = Y |
|||
while i do |
|||
i -= 1; tX += x; tY += y; |
|||
if tX<1 or tY<1 or tX>W or tY>H or board[tY,tX]=' ' then return false end if |
|||
end while |
|||
return true; |
|||
end function |
|||
procedure execute(integer x, y) |
|||
integer ch = board[Y+y,X+x], |
|||
i = iff(ch=' '?0:ch-'0') |
|||
if countSteps(i, x, y) then |
|||
score += i |
|||
while i do |
|||
i -= 1; X += x; Y += y; |
|||
board[Y,X] = ' '; |
|||
end while |
|||
end if |
|||
end procedure |
|||
procedure getInput() |
|||
while true do |
|||
integer k = upper(wait_key()) |
|||
if k='Q' and X > 1 and Y > 1 then execute(-1,-1) exit |
|||
elsif k='W' and Y > 1 then execute( 0,-1) exit |
|||
elsif k='E' and X < W and Y > 1 then execute( 1,-1) exit |
|||
elsif k='A' and X > 1 then execute(-1, 0) exit |
|||
elsif k='D' and X < W then execute( 1, 0) exit |
|||
elsif k='Z' and X > 1 and Y < H then execute(-1, 1) exit |
|||
elsif k='X' and Y < H then execute( 0, 1) exit |
|||
elsif k='C' and X < W and Y < H then execute( 1, 1) exit |
|||
end if |
|||
end while |
|||
printScore(); |
|||
end procedure |
|||
function existsMoves() |
|||
for y=-1 to +1 do |
|||
for x=-1 to +1 do |
|||
if (x or y) |
|||
and X+x>=1 and X+x<=W |
|||
and Y+y>=1 and Y+y<=H then |
|||
integer ch = board[Y+y,X+x]; |
|||
if ch!=' ' and countSteps(ch-'0', x, y) then |
|||
return true |
|||
end if |
|||
end if |
|||
end for |
|||
end for |
|||
return false; |
|||
end function |
|||
procedure play() |
|||
while true do |
|||
cursor(NO_CURSOR); createBoard(); |
|||
while true do |
|||
displayBoard(); getInput() |
|||
if not existsMoves() then exit end if |
|||
end while |
|||
displayBoard(); text_color(7); |
|||
position( 8,19); puts(1,"+----------------------------------------+"); |
|||
position( 9,19); puts(1,"| GAME OVER |"); |
|||
position(10,19); puts(1,"| PLAY AGAIN(Y/N)? |"); |
|||
position(11,19); puts(1,"+----------------------------------------+"); |
|||
position(10,48); cursor(BLOCK_CURSOR); |
|||
if upper(wait_key())!='Y' then return end if |
|||
end while |
|||
end procedure |
|||
play()</syntaxhighlight> |
|||
=={{header|PicoLisp}}== |
|||
Computer play by selecting random road. [https://asciinema.org/a/369181 Demo] is here. |
|||
<syntaxhighlight lang="picolisp"> |
|||
(load "@lib/simul.l") |
|||
(seed (in "/dev/urandom" (rd 8))) |
|||
(scl 6) |
|||
# N - number |
|||
# C - Color |
|||
# F - flag to draw candidates |
|||
# A - @ mark |
|||
(de display () |
|||
(let P 0 |
|||
(wait 500) |
|||
(prin "^[[2J") |
|||
(for L G |
|||
(for This L |
|||
# count cleared cells |
|||
(and (lt0 (: N)) (inc 'P)) |
|||
(prin |
|||
"^[[0;" |
|||
(if (or (: A) (: F)) 100 (: C)) |
|||
"m" |
|||
(cond |
|||
((: A) "@") |
|||
((lt0 (: N)) " ") |
|||
(T (: N)) ) |
|||
"^[[0m" ) ) |
|||
(prinl) ) |
|||
(prinl |
|||
"Score: " |
|||
S |
|||
" " |
|||
(round (*/ P 1.0 100.0 1738.0) 2) |
|||
"%" ) ) ) |
|||
(de roads (Lst Flg) |
|||
(mapc |
|||
'((L) |
|||
(with C |
|||
(do (car L) |
|||
(setq This ((cadr L) This)) |
|||
(=: F Flg) ) ) ) |
|||
Lst ) |
|||
(display) ) |
|||
(let |
|||
(Colors (simul~shuffle (31 32 33 35 91 92 93 94 96)) |
|||
G (simul~grid 22 79) |
|||
C NIL |
|||
S 0 ) |
|||
# set random grid |
|||
(for L G |
|||
(for This L |
|||
(let X (rand 1 9) |
|||
(=: N X) |
|||
(=: C (get Colors X)) ) ) ) |
|||
# set random startpoint |
|||
(with (get G (rand 1 22) (rand 1 79)) |
|||
(setq C This) |
|||
(=: A 0) ) |
|||
(display) |
|||
(loop |
|||
(NIL |
|||
(setq Z |
|||
(extract |
|||
'((D) |
|||
(with C |
|||
(let? S (with (D This) (: N)) |
|||
(and |
|||
(do S |
|||
(NIL (setq This (D This))) |
|||
(NIL (gt0 (: N))) |
|||
'next ) |
|||
(list S D This) ) ) ) ) |
|||
'(simul~west |
|||
simul~east |
|||
simul~south |
|||
simul~north |
|||
((X) (simul~south (simul~west X))) |
|||
((X) (simul~north (simul~west X))) |
|||
((X) (simul~south (simul~east X))) |
|||
((X) (simul~north (simul~east X))) ) ) ) ) |
|||
# XXX |
|||
(roads Z T) |
|||
(roads Z) |
|||
# select road randomly |
|||
(let L (get Z (rand 1 (length Z))) |
|||
(with C |
|||
(inc 'S (: N)) |
|||
# clear value of "old" Center |
|||
(=: N -1) |
|||
(=: A) |
|||
# clear selected road |
|||
(do (car L) |
|||
(inc 'S (: N)) |
|||
(=: N -1) |
|||
(setq This ((cadr L) This)) ) ) |
|||
# set new Center |
|||
(with (caddr L) |
|||
(setq C This) |
|||
(=: A 0) ) ) |
|||
(display) ) ) |
|||
(bye)</syntaxhighlight> |
|||
=={{header|Raku}}== |
|||
{{trans|Phix}} |
|||
<syntaxhighlight lang="raku" line># 20200913 added Raku programming solution |
|||
srand 123456; |
|||
my @board = [ (1..9).roll xx my \w = 79 ] xx my \h = 22 ; |
|||
my \X = $ = w.rand.Int ; my \Y = $ = h.rand.Int; |
|||
@board[Y;X] = '@'; |
|||
my \score = $ = 0; |
|||
sub execute (\y,\x) { |
|||
my \i = $ = @board[Y+y;X+x]; |
|||
if countSteps(i, x, y) { |
|||
score += i; |
|||
@board[ Y + y*$_ ; X + x*$_ ] = ' ' for ^i; |
|||
@board[ Y += y*i ; X += x*i ] = '@'; |
|||
} |
|||
} |
|||
sub countSteps(\i, \x, \y) { |
|||
my \tX = $ = X ; my \tY = $ = Y; |
|||
for ^i { |
|||
tX += x; tY += y; |
|||
return False if tX < 0 or tY < 0 or tX ≥ w or tY ≥ h or @board[tY;tX] eq ' ' |
|||
} |
|||
return True; |
|||
} |
|||
sub existsMoves { |
|||
for (-1 .. 1) X (-1 .. 1) -> (\x,\y) { |
|||
next if x == 0 and y == 0; |
|||
next if X+x < 0 or X+x > w or Y+y < 0 or Y+y > h ; |
|||
my \i = @board[Y+y;X+x]; |
|||
return True if ( i ne ' ' and countSteps(i, x, y) ) |
|||
} |
|||
return False; |
|||
} |
|||
loop { |
|||
for @board { .join.print ; print "\r\n" } ; |
|||
{ say "Game over." and last } unless existsMoves(); |
|||
print "Current score : ", score, "\r\n"; |
|||
given my $c = $*IN.getc { |
|||
when 'q' { say "So long." and last} |
|||
when 'e' { execute(-1,-1) if X > 0 and Y > 0 } # North-West |
|||
when 'r' { execute(-1, 0) if Y > 0 } # North |
|||
when 't' { execute(-1, 1) if X < w and Y > 0 } # North-East |
|||
when 'd' { execute( 0,-1) if X > 0 } # West |
|||
when 'g' { execute( 0, 1) if X < w } # East |
|||
when 'x' { execute( 1,-1) if X > 0 and Y < h } # South-West |
|||
when 'c' { execute( 1, 0) if Y < h } # South |
|||
when 'v' { execute( 1, 1) if X < w and Y < h } # South-East |
|||
} |
|||
}</syntaxhighlight> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
Line 351: | Line 1,373: | ||
No attempt was made to validate the input the input arguments (parameters) for this REXX program. |
No attempt was made to validate the input the input arguments (parameters) for this REXX program. |
||
<lang rexx>/*REXX program lets a user play the game of GREED (by Matthew Day) from the console. */ |
|||
Pointers (above and to the right of) the grid are included to help identify where the current location is. |
|||
<syntaxhighlight lang="rexx">/*REXX program lets a user play the game of GREED (by Matthew Day) from the console. */ |
|||
parse arg sw sd @ b ?r . /*obtain optional argumenst from the CL*/ |
parse arg sw sd @ b ?r . /*obtain optional argumenst from the CL*/ |
||
if sw=='' | sw=="," then sw= |
if sw=='' | sw=="," then sw= 79 /*cols specified? Then use the default*/ |
||
if sd=='' | sd=="," then sd= |
if sd=='' | sd=="," then sd= 22 /*rows " " " " " */ |
||
if @=='' | @=="," then @= '@' /*here " " " " " */ |
if @=='' | @=="," then @= '@' /*here " " " " " */ |
||
if b=='' | b=="," then b= ' ' /*blank " " " " " */ |
if b=='' | b=="," then b= ' ' /*blank " " " " " */ |
||
Line 360: | Line 1,384: | ||
if length(@)==2 & datatype(@,'X') then @=x2c(@) /*maybe use @ char for current pos. */ |
if length(@)==2 & datatype(@,'X') then @=x2c(@) /*maybe use @ char for current pos. */ |
||
if length(b)==2 & datatype(b,'X') then b=x2c(b) /* " " B char for background. */ |
if length(b)==2 & datatype(b,'X') then b=x2c(b) /* " " B char for background. */ |
||
signal on halt /*handle pressing of Ctrl-Break key. */ |
|||
call init |
|||
call init /* [↓] CLR is reset if there's an err*/ |
|||
clr=1; do until # == sw*sd; ??= /*keep playing until the grid is blank.*/ |
|||
call |
call show clr /*show the playing field (grid) to term*/ |
||
call ask; clr= 1 /*obtain user's move, validate, or quit*/ |
|||
if \move() then do; clr= 0 /*perform the user's move per @ loc.*/ |
|||
if ??==@. then say ____ "invalid move: moving out of bounds." |
|||
if ??==b then say ____ "invalid move: moving into a blank." |
|||
end |
|||
call show 0 |
|||
end /*until*/ /* [↑] Also, if out─of─bounds, LEAVE. */ |
end /*until*/ /* [↑] Also, if out─of─bounds, LEAVE. */ |
||
if show(1)==sw*sd then say ____ "You've won, the grid is blank, your score is: " # |
if show(1)==sw*sd then say ____ "You've won, the grid is blank, your score is: " # |
||
if @.!r.!c==@. then say ____ "Game over (out─of─bounds), your score is: " # |
|||
if @.!r.!c==b then say ____ "Game over (a blank location), your score is: " # |
|||
exit 2 |
exit 2 |
||
exit 0 /*stick a fork in it, we're all done. */ |
exit 0 /*stick a fork in it, we're all done. */ |
||
Line 377: | Line 1,404: | ||
say ____ 'moves:' ____ ' Z= ◄↓ X= ↓ C= ►↓' |
say ____ 'moves:' ____ ' Z= ◄↓ X= ↓ C= ►↓' |
||
say ____ |
say ____ |
||
say ____ 'enter a move ──or── |
say ____ 'enter a move ──or── QUIT (the score is: ' #")" |
||
parse pull |
parse pull z 2 1 what . 1 oz; upper z what |
||
if abbrev('QUIT', |
if abbrev('QUIT', what, 2) | abbrev("QQUIT", what, 2) then leave |
||
if length( space( |
if length( space(oz) )==1 & pos(z, 'QWEADZXC')\==0 then return |
||
say ____ '***error*** invalid move:' space( |
say ____ '***error*** invalid direction for a move:' space(oz); say |
||
end /*forever*/ |
end /*forever*/ |
||
halt: say; say ____ 'quitting.'; exit 1 |
halt: say; say ____ 'quitting.'; exit 1 |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
init: @.= ' |
init: @.= 'ff'x; $.=.; ____= copies("─", 8) /*out─of─bounds literal; fence for SAYs*/ |
||
signal on halt /*handle pressing of Ctrl-Break key. */ |
|||
do r=1 for sd |
do r=1 for sd |
||
do c=1 for sw; @.r.c=random(1, 9) |
do c=1 for sw; @.r.c= random(1, 9) /*assign grid area to random digs (1►9)*/ |
||
end /*c*/ |
end /*c*/ |
||
end /*r*/ |
end /*r*/ |
||
!r=random(1, sd); !c=random(1, sw); @.!r.!c=@; return |
!r= random(1, sd); !c= random(1, sw); @.!r.!c= @; return /*assign 1st position*/ |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
move: @.!r.!c= |
move: @.!r.!c= '≤'; $r= !r; $c= !c; ??= /*blank out this "start" position. */ |
||
@@=. /*nullify the count of move positions. */ |
@@=. /*nullify the count of move positions. */ |
||
do until @@==0; |
do until @@==0; select |
||
when z== 'Q' then do; !r= !r - 1; !c= !c - 1; end |
|||
when z== 'W' then !r= !r - 1 |
|||
when z== 'E' then do; !r= !r - 1; !c= !c + 1; end |
|||
when z== 'A' then !c= !c - 1 |
|||
when z== 'D' then !c= !c + 1 |
|||
when z== 'Z' then do; !r= !r + 1; !c= !c - 1; end |
|||
when z== 'X' then !r= !r + 1 |
|||
when z== 'C' then do; !r= !r + 1; !c= !c + 1; end |
|||
end /*select*/ |
|||
?=@.!r.!c; if ?==@. | ?==b then |
?= @.!r.!c; if ?==@. | ?==b then do; !r= $r; !c= $c; ??= ?; return 0 |
||
end |
|||
@. |
if @@==. then @@=?; if datatype(@@, 'W') then @@= @@ - 1 /*diminish cnt.*/ |
||
@.!r.!c= '±' /*nullify (later, a blank) position. */ |
|||
end /*until*/ |
end /*until*/ |
||
@.!r.!c=@; return 1 |
@.!r.!c= @; return 1 /*signify current grid position with @ */ |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
show: arg tell; #=0; if tell then do; |
show: arg tell; #=0; if tell then do; "CLS"; say left('', !c)"↓"; end |
||
do r=1 for sd; _= |
do r=1 for sd; _= ' ' /* [↑] DOS cmd CLS clears the screen*/ |
||
do c=1 for sw |
do c=1 for sw /*construct row of the grid for display*/ |
||
if |
if @.r.c=="±" & ??\=='' then @.r.c= $.r.c /*Is this a temp blank? Restore*/ |
||
if @.r.c=="±" & ?? =='' then @.r.c= b /*Is this a temp blank? Blank. */ |
|||
if @.r.c=="≤" & ??\=='' then @.r.c= $.r.c /*Is this a temp a @ ? Restore*/ |
|||
if @.r.c=="≤" & ?? =='' then @.r.c= b /*Is this a temp a @ ? Blank. */ |
|||
?= @.r.c; _= _ || ? /*construct a line of the grid for term*/ |
|||
if ?==b | ?==@ then #= # + 1 /*Position==b ─or─ @? Then bump score.*/ |
|||
if tell then $.r.c= @.r.c /*create a backup grid for re─instating*/ |
|||
end /*c*/ |
end /*c*/ |
||
if r==!r then _=_ '◄' |
if r==!r then _= _ '◄' /*indicate row of current position. */ |
||
if tell then say _ /*display a row of grid to screen. */ |
if tell then say _ /*display a row of grid to screen. */ |
||
end /*r*/; say; |
end /*r*/; say; return # /*SHOW also counts # of blanks (score).*/</syntaxhighlight> |
||
A note on the OUTPUT sections: each screen displayed is shown below as a separate OUTPUT section. |
A note on the OUTPUT sections: each (cleared) screen displayed is shown below as a separate OUTPUT section. |
||
The following are the screen shots when inputs used (size of the grid) are: <tt> 22 10 </tt> |
The following are the screen shots when inputs used (size of the grid) are: <tt> 22 10 </tt> |
||
Line 425: | Line 1,458: | ||
{{out|output|text=: the 1<sup>st</sup> screen shown.}} |
{{out|output|text=: the 1<sup>st</sup> screen shown.}} |
||
<pre> |
<pre> |
||
↓ |
↓ |
||
1636166561333644938615925878672969839136949348125385742112849651343354296271245 |
1636166561333644938615925878672969839136949348125385742112849651343354296271245 |
||
4935939188413836477495369151362748736256329449564639583731265554747438655579797 |
4935939188413836477495369151362748736256329449564639583731265554747438655579797 |
||
2761827294343258918258167935625127433626644177165772453435474591949917695547965 |
2761827294343258918258167935625127433626644177165772453435474591949917695547965 |
||
5336784646373682676398688475989972451499776252164989899239191733912697265898925 |
5336784646373682676398688475989972451499776252164989899239191733912697265898925 |
||
4952948995581413589577455233495962736898536553933712711747529619371573895413265 |
4952948995581413589577455233495962736898536553933712711747529619371573895413265 |
||
5328643745672485468516645326176482571162377128958669252244431799914145324756787 |
5328643745672485468516645326176482571162377128958669252244431799914145324756787 |
||
9682648416475828434376154259111596818112819626518754715385939211764235211148126 |
9682648416475828434376154259111596818112819626518754715385939211764235211148126 |
||
4771918124154627513339665771138169237888886368882335865655526894655352121961215 |
4771918124154627513339665771138169237888886368882335865655526894655352121961215 |
||
794644718989445262471866768299551827168758297323537929749@815519895387457566428 ◄ |
794644718989445262471866768299551827168758297323537929749@815519895387457566428 ◄ |
||
9347969832617624113866732722842121521854745888458198852913265875445986923272597 |
9347969832617624113866732722842121521854745888458198852913265875445986923272597 |
||
──────── moves: ──────── Q= ◄↑ W= ↑ E= ►↑ |
──────── moves: ──────── Q= ◄↑ W= ↑ E= ►↑ |
||
Line 446: | Line 1,479: | ||
{{out|output|text=: the 2<sup>nd</sup> screen shown.}} |
{{out|output|text=: the 2<sup>nd</sup> screen shown.}} |
||
<pre> |
<pre> |
||
↓ |
↓ |
||
1636166561333644938615925878672969839136949348125385742112849651343354296271245 |
1636166561333644938615925878672969839136949348125385742112849651343354296271245 |
||
4935939188413836477495369151362748736256329449564639583731265554747438655579797 |
4935939188413836477495369151362748736256329449564639583731265554747438655579797 |
||
2761827294343258918258167935625127433626644177165772453435474591949917695547965 |
2761827294343258918258167935625127433626644177165772453435474591949917695547965 |
||
53367846463736826763986884759899724514997762521649898992391917@3912697265898925 ◄ |
53367846463736826763986884759899724514997762521649898992391917@3912697265898925 ◄ |
||
4952948995581413589577455233495962736898536553933712711747529 19371573895413265 |
4952948995581413589577455233495962736898536553933712711747529 19371573895413265 |
||
532864374567248546851664532617648257116237712895866925224443 799914145324756787 |
532864374567248546851664532617648257116237712895866925224443 799914145324756787 |
||
96826484164758284343761542591115968181128196265187547153859 9211764235211148126 |
96826484164758284343761542591115968181128196265187547153859 9211764235211148126 |
||
4771918124154627513339665771138169237888886368882335865655 26894655352121961215 |
4771918124154627513339665771138169237888886368882335865655 26894655352121961215 |
||
794644718989445262471866768299551827168758297323537929749 815519895387457566428 |
794644718989445262471866768299551827168758297323537929749 815519895387457566428 |
||
9347969832617624113866732722842121521854745888458198852913265875445986923272597 |
9347969832617624113866732722842121521854745888458198852913265875445986923272597 |
||
──────── moves: ──────── Q= ◄↑ W= ↑ E= ►↑ |
──────── moves: ──────── Q= ◄↑ W= ↑ E= ►↑ |
||
Line 463: | Line 1,496: | ||
──────── |
──────── |
||
──────── enter a move ──or── enter QUIT to quit. (score is: 6) |
──────── enter a move ──or── enter QUIT to quit. (score is: 6) |
||
quit ◄■■■■■■■■■■■■■ user input |
|||
──────── quitting. |
|||
</pre> |
</pre> |
||
{{out|output|text=: the 3<sup>rd</sup> screen shown.}} |
|||
<pre> |
|||
↓ |
|||
1636166561333644938615925878672969839136949348125385742112849651343354296271245 |
|||
4935939188413836477495369151362748736256329449564639583731265554747438655579797 |
|||
2761827294343258918258167935625127433626644177165772453435474591949917695547965 |
|||
53367846463736826763986884759899724514997762521649898992391917 3912697265898925 |
|||
4952948995581413589577455233495962736898536553933712711747529 1 371573895413265 |
|||
532864374567248546851664532617648257116237712895866925224443 799 14145324756787 |
|||
96826484164758284343761542591115968181128196265187547153859 92117 4235211148126 |
|||
4771918124154627513339665771138169237888886368882335865655 2689465 352121961215 |
|||
794644718989445262471866768299551827168758297323537929749 815519895 87457566428 |
|||
93479698326176241138667327228421215218547458884581988529132658754459 6923272597 |
|||
──────── Game over (out─of─bounds), your score is: 12 |
|||
=={{header|Wren}}== |
|||
<pre> |
|||
See [[Greed/Wren]]. |
|||
=={{header|Z80 Assembly}}== |
|||
See [[Greed/Z80 Assembly]]. |
|||
<br><br> |
Latest revision as of 14:30, 27 August 2022
This task is about making a clone of the game "GREED" by Matthew Day.
This game is played on a grid of 79 column by 22 rows of random numbers from 1 to 9 (inclusive).
The player location is signified by the @ symbol.
The object of Greed is to erase as much of the screen as possible by moving around (all 8 directions are allowed in this grid).
When you move in a direction, N number of grid squares are erased ("eaten") in that direction, N being the first number in that direction.
Your score reflects the total number of squares "eaten".
You may not make a move that places you off the grid or over a previously eaten square.
The game is over if there is no more valid moves.
C++
Windows console version.
![](http://static.miraheze.org/rosettacodewiki/thumb/3/3d/NewGreedCpp.png/200px-NewGreedCpp.png)
#include <windows.h>
#include <iostream>
#include <ctime>
const int WID = 79, HEI = 22;
const float NCOUNT = ( float )( WID * HEI );
class coord : public COORD {
public:
coord( short x = 0, short y = 0 ) { set( x, y ); }
void set( short x, short y ) { X = x; Y = y; }
};
class winConsole {
public:
static winConsole* getInstamnce() { if( 0 == inst ) { inst = new winConsole(); } return inst; }
void showCursor( bool s ) { CONSOLE_CURSOR_INFO ci = { 1, s }; SetConsoleCursorInfo( conOut, &ci ); }
void setColor( WORD clr ) { SetConsoleTextAttribute( conOut, clr ); }
void setCursor( coord p ) { SetConsoleCursorPosition( conOut, p ); }
void flush() { FlushConsoleInputBuffer( conIn ); }
void kill() { delete inst; }
private:
winConsole() { conOut = GetStdHandle( STD_OUTPUT_HANDLE );
conIn = GetStdHandle( STD_INPUT_HANDLE ); showCursor( false ); }
static winConsole* inst;
HANDLE conOut, conIn;
};
class greed {
public:
greed() { console = winConsole::getInstamnce(); }
~greed() { console->kill(); }
void play() {
char g; do {
console->showCursor( false ); createBoard();
do { displayBoard(); getInput(); } while( existsMoves() );
displayBoard(); console->setCursor( coord( 0, 24 ) ); console->setColor( 0x07 );
console->setCursor( coord( 19, 8 ) ); std::cout << "+----------------------------------------+";
console->setCursor( coord( 19, 9 ) ); std::cout << "| GAME OVER |";
console->setCursor( coord( 19, 10 ) ); std::cout << "| PLAY AGAIN(Y/N)? |";
console->setCursor( coord( 19, 11 ) ); std::cout << "+----------------------------------------+";
console->setCursor( coord( 48, 10 ) ); console->showCursor( true ); console->flush(); std::cin >> g;
} while( g == 'Y' || g == 'y' );
}
private:
void createBoard() {
for( int y = 0; y < HEI; y++ ) {
for( int x = 0; x < WID; x++ ) {
brd[x + WID * y] = rand() % 9 + 1;
}
}
cursor.set( rand() % WID, rand() % HEI );
brd[cursor.X + WID * cursor.Y] = 0; score = 0;
printScore();
}
void displayBoard() {
console->setCursor( coord() ); int i;
for( int y = 0; y < HEI; y++ ) {
for( int x = 0; x < WID; x++ ) {
i = brd[x + WID * y]; console->setColor( 6 + i );
if( !i ) std::cout << " "; else std::cout << i;
}
std::cout << "\n";
}
console->setColor( 15 ); console->setCursor( cursor ); std::cout << "@";
}
void getInput() {
while( 1 ) {
if( ( GetAsyncKeyState( 'Q' ) & 0x8000 ) && cursor.X > 0 && cursor.Y > 0 ) { execute( -1, -1 ); break; }
if( ( GetAsyncKeyState( 'W' ) & 0x8000 ) && cursor.Y > 0 ) { execute( 0, -1 ); break; }
if( ( GetAsyncKeyState( 'E' ) & 0x8000 ) && cursor.X < WID - 1 && cursor.Y > 0 ) { execute( 1, -1 ); break; }
if( ( GetAsyncKeyState( 'A' ) & 0x8000 ) && cursor.X > 0 ) { execute( -1, 0 ); break; }
if( ( GetAsyncKeyState( 'D' ) & 0x8000 ) && cursor.X < WID - 1 ) { execute( 1, 0 ); break; }
if( ( GetAsyncKeyState( 'Y' ) & 0x8000 ) && cursor.X > 0 && cursor.Y < HEI - 1 ) { execute( -1, 1 ); break; }
if( ( GetAsyncKeyState( 'X' ) & 0x8000 ) && cursor.Y < HEI - 1 ) { execute( 0, 1 ); break; }
if( ( GetAsyncKeyState( 'C' ) & 0x8000 ) && cursor.X < WID - 1 && cursor.Y < HEI - 1 ) { execute( 1, 1 ); break; }
}
console->flush(); printScore();
}
void printScore() {
console->setCursor( coord( 0, 24 ) ); console->setColor( 0x2a );
std::cout << " SCORE: " << score << " : " << score * 100.f / NCOUNT << "% ";
}
void execute( int x, int y ) {
int i = brd[cursor.X + x + WID * ( cursor.Y + y )];
if( countSteps( i, x, y ) ) {
score += i;
while( i ) {
--i; cursor.X += x; cursor.Y += y;
brd[cursor.X + WID * cursor.Y] = 0;
}
}
}
bool countSteps( int i, int x, int y ) {
coord t( cursor.X, cursor.Y );
while( i ) {
--i; t.X += x; t.Y += y;
if( t.X < 0 || t.Y < 0 || t.X >= WID || t.Y >= HEI || !brd[t.X + WID * t.Y] ) return false;
}
return true;
}
bool existsMoves() {
int i;
for( int y = -1; y < 2; y++ ) {
for( int x = -1; x < 2; x++ ) {
if( !x && !y ) continue;
i = brd[cursor.X + x + WID * ( cursor.Y + y )];
if( i > 0 && countSteps( i, x, y ) ) return true;
}
}
return false;
}
winConsole* console;
int brd[WID * HEI];
float score; coord cursor;
};
winConsole* winConsole::inst = 0;
int main( int argc, char* argv[] ) {
srand( ( unsigned )time( 0 ) );
SetConsoleTitle( "Greed" );
greed g; g.play(); return 0;
}
Factor
This uses Factor's own user interface vocabularies. Use hjkl-bnyu (vi-keys) to move.
USING: accessors arrays colors combinators
combinators.short-circuit fry grouping io io.styles kernel lexer
literals make math math.matrices math.parser math.vectors random
sequences strings ui ui.commands ui.gadgets.panes
ui.gadgets.status-bar ui.gadgets.worlds ui.gestures
ui.pens.solid ;
IN: rosetta-code.greed
<<
SYNTAX: RGB: scan-token 2 cut 2 cut [ hex> 255 /f ] tri@ 1
<rgba> suffix! ;
>>
CONSTANT: cells-width 79
CONSTANT: cells-height 22
CONSTANT: size 24
CONSTANT: bg-color RGB: 000000
CONSTANT: player-format {
{ font-size $ size }
{ foreground RGB: 5990C8 }
{ background RGB: B96646 }
}
CONSTANT: normal-format { { font-size $ size } }
CONSTANT: colors {
RGB: 40B4A4
RGB: 40B3B7
RGB: 40A2B9
RGB: 408FBC
RGB: 407CBF
RGB: 4268C0
RGB: 4355C2
RGB: 4845C3
RGB: 5F46C4
}
CONSTANT: neighbors {
{ -1 -1 } { -1 0 } { -1 1 }
{ 0 -1 } { 0 1 }
{ 1 -1 } { 1 0 } { 1 1 }
}
TUPLE: greed < pane cells x y score ;
: set-player ( greed elt -- )
'[ y>> _ swap ] [ x>> 2array ] [ cells>> ] tri set-index ;
: place-player ( greed -- ) 0 set-player ;
: remove-player ( greed -- ) f set-player ;
: make-cells ( -- cells )
cells-width cells-height * [ 9 random 1 + ] replicate
cells-width group ;
: write-number ( n/f -- )
[ >digit 1string normal-format first foreground ]
[ 1 - colors nth 2array ] bi 2array format ;
: write-cell ( n/f -- )
{
{ f [ " " normal-format format ] }
{ 0 [ "@" player-format format ] }
[ write-number ]
} case ;
: write-cells ( cells -- ) [ [ write-cell ] each nl ] each ;
: update-cells ( greed -- )
dup cells>> [ write-cells ] curry with-pane ;
: init-greed ( greed -- greed' )
make-cells >>cells cells-width random >>x cells-height
random >>y 0 >>score dup place-player dup update-cells dup
"Score: 0" swap show-status ;
: <greed> ( -- greed )
f greed new-pane bg-color <solid> >>interior init-greed ;
: ?r,c ( r c matrix -- elt/f ) swapd ?nth ?nth ;
: ?r,cths ( seq matrix -- newseq )
[ [ first2 ] dip ?r,c ] curry map ;
: (ray) ( start-loc dir length -- seq )
1 + [ [ [ v+ ] keep over , ] times ] { } make 2nip ;
: ray ( start-loc dir length -- seq/f )
dup [ (ray) ] [ 2nip ] if ;
: ?r,c-dir ( r c dir matrix -- n )
[ 2array ] [ v+ first2 ] [ ?r,c ] tri* ;
: move-length ( greed dir -- n )
[ [ y>> ] [ x>> ] [ ] tri ] dip swap cells>> ?r,c-dir ;
: y,x>loc ( greed -- loc ) [ y>> ] [ x>> ] bi 2array ;
: ray-dir ( greed dir -- seq )
[ [ y,x>loc ] dip ] [ move-length ] 2bi ray ;
: in-bounds? ( dim loc -- ? )
{ [ nip [ 0 >= ] all? ] [ v- [ 0 > ] all? ] } 2&& ;
: endpoint-in-bounds? ( greed dir -- ? )
ray-dir dup [
last ${ cells-height cells-width } swap in-bounds?
] when ;
: gapless? ( greed dir -- ? )
[ ray-dir ] [ drop cells>> ?r,cths ] 2bi [ integer? ] all? ;
: can-move? ( greed dir -- ? )
{ [ endpoint-in-bounds? ] [ gapless? ] } 2&& ;
: can-move-any? ( greed -- ? )
neighbors [ can-move? ] with map [ t = ] any? ;
: setup-move ( greed dir -- seq ) over remove-player ray-dir ;
: update-score ( greed dir -- greed dir )
2dup move-length pick swap [ + ] curry change-score dup
score>> number>string "Score: " prepend swap show-status ;
: (move) ( greed dir -- )
update-score [ drop f ] [ setup-move dup last ]
[ drop cells>> swap [ set-indices ] dip ] 2tri first2
[ >>y ] dip >>x place-player ;
: game-over ( greed -- )
[
score>> number>string "Game over! Final score: "
prepend " Press <space> for new game." append
] [ show-status ] bi ;
: ?game-over ( greed -- )
dup can-move-any? [ drop ] [ game-over ] if ;
: move ( greed dir -- )
dupd 2dup can-move? [ (move) ] [ 2drop ] if
[ update-cells ] [ ?game-over ] bi ;
: ?new-game ( greed -- )
dup can-move-any? [ drop ] [ init-greed drop ] if ;
: e ( greed -- ) { 0 1 } move ;
: se ( greed -- ) { 1 1 } move ;
: s ( greed -- ) { 1 0 } move ;
: sw ( greed -- ) { 1 -1 } move ;
: w ( greed -- ) { 0 -1 } move ;
: nw ( greed -- ) { -1 -1 } move ;
: n ( greed -- ) { -1 0 } move ;
: ne ( greed -- ) { -1 1 } move ;
greed "gestures" f {
{ T{ key-down { sym "l" } } e }
{ T{ key-down { sym "n" } } se }
{ T{ key-down { sym "j" } } s }
{ T{ key-down { sym "b" } } sw }
{ T{ key-down { sym "h" } } w }
{ T{ key-down { sym "y" } } nw }
{ T{ key-down { sym "k" } } n }
{ T{ key-down { sym "u" } } ne }
{ T{ key-down { sym " " } } ?new-game }
} define-command-map
: greed-window ( -- )
[
<greed> <world-attributes> "Greed" >>title
open-status-window
] with-ui ;
MAIN: greed-window
- Output:
Screenshot of the game after a loss
Go
This hasn't been tested on Windows 10 but should work.
Note that this version uses the Z key (rather than the Y key) to move diagonally downwards to the left. A leave key, L, has also been added in case one wants to end the game prematurely.
package main
import (
"fmt"
"github.com/nsf/termbox-go"
"log"
"math/rand"
"strconv"
"time"
)
type coord struct{ x, y int }
const (
width = 79
height = 22
nCount = float64(width * height)
)
var (
board [width * height]int
score = 0
bold = termbox.AttrBold
cursor coord
)
var colors = [10]termbox.Attribute{
termbox.ColorDefault,
termbox.ColorWhite,
termbox.ColorBlack | bold,
termbox.ColorBlue | bold,
termbox.ColorGreen | bold,
termbox.ColorCyan | bold,
termbox.ColorRed | bold,
termbox.ColorMagenta | bold,
termbox.ColorYellow | bold,
termbox.ColorWhite | bold,
}
func printAt(x, y int, s string, fg, bg termbox.Attribute) {
for _, r := range s {
termbox.SetCell(x, y, r, fg, bg)
x++
}
}
func createBoard() {
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
board[x+width*y] = rand.Intn(9) + 1
}
}
cursor = coord{rand.Intn(width), rand.Intn(height)}
board[cursor.x+width*cursor.y] = 0
score = 0
printScore()
}
func displayBoard() {
termbox.SetCursor(0, 0)
bg := colors[0]
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
i := board[x+width*y]
fg := colors[i]
s := " "
if i > 0 {
s = strconv.Itoa(i)
}
printAt(x, y, s, fg, bg)
}
}
fg := colors[9]
termbox.SetCursor(cursor.x, cursor.y)
printAt(cursor.x, cursor.y, "@", fg, bg)
termbox.Flush()
}
func printScore() {
termbox.SetCursor(0, 24)
fg := colors[4]
bg := termbox.ColorGreen
s := fmt.Sprintf(" SCORE: %d : %.3f%% ", score, float64(score)*100.0/nCount)
printAt(0, 24, s, fg, bg)
termbox.Flush()
}
func execute(x, y int) {
i := board[cursor.x+x+width*(cursor.y+y)]
if countSteps(i, x, y) {
score += i
for i != 0 {
i--
cursor.x += x
cursor.y += y
board[cursor.x+width*cursor.y] = 0
}
}
}
func countSteps(i, x, y int) bool {
t := cursor
for i != 0 {
i--
t.x += x
t.y += y
if t.x < 0 || t.y < 0 || t.x >= width || t.y >= height || board[t.x+width*t.y] == 0 {
return false
}
}
return true
}
func existsMoves() bool {
for y := -1; y < 2; y++ {
for x := -1; x < 2; x++ {
if x == 0 && y == 0 {
continue
}
ix := cursor.x + x + width*(cursor.y+y)
i := 0
if ix >= 0 && ix < len(board) {
i = board[ix]
}
if i > 0 && countSteps(i, x, y) {
return true
}
}
}
return false
}
func check(err error) {
if err != nil {
log.Fatal(err)
}
}
func main() {
rand.Seed(time.Now().UnixNano())
err := termbox.Init()
check(err)
defer termbox.Close()
eventQueue := make(chan termbox.Event)
go func() {
for {
eventQueue <- termbox.PollEvent()
}
}()
for {
termbox.HideCursor()
createBoard()
for {
displayBoard()
select {
case ev := <-eventQueue:
if ev.Type == termbox.EventKey {
switch ev.Ch {
case 'q', 'Q':
if cursor.x > 0 && cursor.y > 0 {
execute(-1, -1)
}
case 'w', 'W':
if cursor.y > 0 {
execute(0, -1)
}
case 'e', 'E':
if cursor.x < width-1 && cursor.y > 0 {
execute(1, -1)
}
case 'a', 'A':
if cursor.x > 0 {
execute(-1, 0)
}
case 'd', 'D':
if cursor.x < width-1 {
execute(1, 0)
}
case 'z', 'Z':
if cursor.x > 0 && cursor.y < height-1 {
execute(-1, 1)
}
case 'x', 'X':
if cursor.y < height-1 {
execute(0, 1)
}
case 'c', 'C':
if cursor.x < width-1 && cursor.y < height-1 {
execute(1, 1)
}
case 'l', 'L': // leave key
return
}
} else if ev.Type == termbox.EventResize {
termbox.Flush()
}
}
printScore()
if !existsMoves() {
break
}
}
displayBoard()
fg := colors[7]
bg := colors[0]
printAt(19, 8, "+----------------------------------------+", fg, bg)
printAt(19, 9, "| GAME OVER |", fg, bg)
printAt(19, 10, "| PLAY AGAIN(Y/N)? |", fg, bg)
printAt(19, 11, "+----------------------------------------+", fg, bg)
termbox.SetCursor(48, 10)
termbox.Flush()
select {
case ev := <-eventQueue:
if ev.Type == termbox.EventKey {
if ev.Ch == 'y' || ev.Ch == 'Y' {
break
} else {
return
}
}
}
}
}
Kotlin
Note that this version uses the Z key (rather than the Y key) to move diagonally downwards to the left.
// Kotlin Native v0.5
import kotlinx.cinterop.*
import platform.posix.*
import platform.windows.*
const val WID = 79
const val HEI = 22
const val NCOUNT = (WID * HEI).toFloat()
class WinConsole {
val conOut: HANDLE
val conIn: HANDLE
private constructor() {
conOut = GetStdHandle(STD_OUTPUT_HANDLE)!!
conIn = GetStdHandle(STD_INPUT_HANDLE)!!
showCursor(FALSE)
}
fun showCursor(s: WINBOOL) {
memScoped {
val ci = alloc<CONSOLE_CURSOR_INFO>().apply { dwSize = 1; bVisible = s }
SetConsoleCursorInfo(conOut, ci.ptr)
}
}
fun setColor(clr: WORD) = SetConsoleTextAttribute(conOut, clr)
fun setCursor(p: COORD) = SetConsoleCursorPosition(conOut, p.readValue())
fun flush() = FlushConsoleInputBuffer(conIn)
fun kill() {
inst = null
}
companion object {
val instance: WinConsole
get() {
if (inst == null) inst = WinConsole()
return inst!!
}
private var inst: WinConsole? = null
}
}
class Greed {
private val console: WinConsole
private val brd = IntArray(WID * HEI)
private var score = 0
private lateinit var cursor: COORD
init {
console = WinConsole.instance
SetConsoleTitleW("Greed")
}
fun destroy() {
nativeHeap.free(cursor)
console.kill()
}
fun play() {
memScoped {
val coord1 = alloc<COORD>().apply { X = 0; Y = 24 }
val coord2 = alloc<COORD>().apply { X = 19; Y = 8 }
val coord3 = alloc<COORD>().apply { X = 19; Y = 9 }
val coord4 = alloc<COORD>().apply { X = 19; Y = 10 }
val coord5 = alloc<COORD>().apply { X = 19; Y = 11 }
val coord6 = alloc<COORD>().apply { X = 48; Y = 10 }
do {
console.showCursor(FALSE)
createBoard()
do {
displayBoard()
getInput()
}
while (existsMoves())
displayBoard()
with (console) {
setCursor(coord1)
setColor(0x07)
setCursor(coord2); print("+----------------------------------------+")
setCursor(coord3); print("| GAME OVER |")
setCursor(coord4); print("| PLAY AGAIN(Y/N)? |")
setCursor(coord5); print("+----------------------------------------+")
setCursor(coord6)
showCursor(TRUE)
flush()
}
val g = readLine()!!.toUpperCase()
}
while (g.length >= 1 && g[0] == 'Y')
}
destroy()
}
private fun createBoard() {
for (y in 0 until HEI) {
for (x in 0 until WID) {
brd[x + WID * y] = rand() % 9 + 1
}
}
cursor = nativeHeap.alloc<COORD>().apply {
X = (rand() % WID).toShort(); Y = (rand() % HEI).toShort()
}
brd[cursor.X + WID * cursor.Y] = 0
score = 0
printScore()
}
private fun displayBoard() {
memScoped {
val coord = alloc<COORD>().apply { X = 0; Y = 0 }
console.setCursor(coord)
}
for (y in 0 until HEI) {
for (x in 0 until WID) {
val i = brd[x + WID * y]
console.setColor((6 + i).toShort())
print(if (i == 0) " " else "$i")
}
println()
}
console.setColor(15)
console.setCursor(cursor)
print("@")
}
private fun checkKey(k: Char) = (GetAsyncKeyState(k.toInt()).toInt() and 0x8000) != 0
private fun getInput() {
while (true) {
if (checkKey('Q') && cursor.X > 0 && cursor.Y > 0) { execute(-1, -1); break }
if (checkKey('W') && cursor.Y > 0) { execute(0, -1); break }
if (checkKey('E') && cursor.X < WID - 1 && cursor.Y > 0) { execute(1, -1); break }
if (checkKey('A') && cursor.X > 0) { execute(-1, 0); break }
if (checkKey('D') && cursor.X < WID - 1) { execute(1, 0); break }
if (checkKey('Z') && cursor.X > 0 && cursor.Y < HEI - 1) { execute(-1, 1); break }
if (checkKey('X') && cursor.Y < HEI - 1) { execute(0, 1); break }
if (checkKey('C') && cursor.X < WID - 1 && cursor.Y < HEI - 1) { execute(1, 1); break }
}
console.flush()
printScore()
}
private fun printScore() {
memScoped {
val coord = alloc<COORD>().apply { X = 0; Y = 24 }
console.setCursor(coord)
}
console.setColor(0x2a)
print(" SCORE: $score : ${score * 100.0f / NCOUNT}% ")
}
private fun execute(x: Int, y: Int) {
var i = brd[cursor.X + x + WID * ( cursor.Y + y )]
if (countSteps(i, x, y)) {
score += i
while (i-- != 0) {
cursor.X = (cursor.X + x).toShort()
cursor.Y = (cursor.Y + y).toShort()
brd[cursor.X + WID * cursor.Y] = 0
}
}
}
private fun countSteps(i: Int, x: Int, y: Int): Boolean {
var ii = i
memScoped {
val t = alloc<COORD>().apply { X = cursor.X; Y = cursor.Y }
while (ii-- != 0) {
t.X = (t.X + x).toShort()
t.Y = (t.Y + y).toShort()
if (t.X < 0 || t.Y < 0 || t.X >= WID || t.Y >= HEI || brd[t.X + WID * t.Y] == 0 ) return false
}
}
return true
}
private fun existsMoves(): Boolean {
for (y in -1..1) {
for (x in -1..1) {
if (x == 0 && y == 0) continue
val i = brd[cursor.X + x + WID * ( cursor.Y + y )]
if (i > 0 && countSteps(i, x, y)) return true
}
}
return false
}
}
fun main(args: Array<String>) {
srand(time(null).toInt())
Greed().play()
}
Java
See Greed/Java.
Julia
GUI version. Click a square adjacent to the "@" symbol to move.
using Gtk
struct BState
board::Matrix{Int}
row::Int
col::Int
end
function greedapp(r, c)
rows, cols = c, r # gtk rotates grid 90 degrees
win = GtkWindow("Greed Game", 1200, 400) |> (GtkFrame() |> (box = GtkBox(:v)))
toolbar = GtkToolbar()
newgame = GtkToolButton("New Game")
set_gtk_property!(newgame, :label, "New Game")
set_gtk_property!(newgame, :is_important, true)
undomove = GtkToolButton("Undo Move")
set_gtk_property!(undomove, :label, "Undo Move")
set_gtk_property!(undomove, :is_important, true)
map(w->push!(toolbar,w),[newgame,undomove])
scrwin = GtkScrolledWindow()
grid = GtkGrid()
map(w -> push!(box, w),[toolbar, scrwin])
push!(scrwin, grid)
buttons = Array{Gtk.GtkButtonLeaf, 2}(undef, rows, cols)
for i in 1:rows, j in 1:cols
grid[i,j] = buttons[i,j] = GtkButton()
set_gtk_property!(buttons[i,j], :expand, true)
end
function findrowcol(button)
for i in 1:rows, j in 1:cols
if buttons[i, j] == button
return i, j
end
end
return 0, 0
end
board = zeros(Int, rows, cols)
pastboardstates = Vector{BState}()
score = 0
condition = Condition()
won = ""
myrow, mycol = 1, 1
function update!()
for i in 1:rows, j in 1:cols
label = (board[i, j] > 0) ? board[i, j] : " "
set_gtk_property!(buttons[i, j], :label, label)
end
set_gtk_property!(buttons[myrow, mycol], :label, "@")
won = all(iszero, board) ? "WINNING" : ""
set_gtk_property!(win, :title, "$won Greed Game (Score: $score)")
end
function erasefromtile!(moverow, movecol)
xdir, ydir = moverow - myrow, movecol - mycol
if abs(xdir) > 1 || abs(ydir) > 1 || 0 == xdir == ydir || board[moverow, movecol] == 0
return
end
push!(pastboardstates, BState(deepcopy(board), myrow, mycol))
for i in 1:board[moverow, movecol]
x, y = myrow + xdir * i, mycol + ydir * i
if 0 < x <= rows && 0 < y <= cols
board[x, y] = 0
score += 1
end
end
board[myrow, mycol] = 0
myrow = moverow
mycol = movecol
update!()
end
clicked(button) = begin x, y = findrowcol(button); erasefromtile!(x, y) end
function initialize!(w)
won = ""
possiblevals = collect(1:9)
for i in 1:rows, j in 1:cols
board[i, j] = rand(possiblevals)
set_gtk_property!(buttons[i,j], :label, board[i, j])
signal_connect(clicked, buttons[i, j], "clicked")
end
myrow = rand(1:rows)
mycol = rand(1:cols)
board[myrow, mycol] = 0
update!()
end
function undo!(w)
if won == "" && length(pastboardstates) > 0
bst = pop!(pastboardstates)
board, myrow, mycol = bst.board, bst.row, bst.col
update!()
end
end
endit(w) = notify(condition)
initialize!(win)
signal_connect(initialize!, newgame, :clicked)
signal_connect(undo!, undomove, :clicked)
signal_connect(endit, win, :destroy)
showall(win)
wait(condition)
end
# greedapp(22, 79) # This would be per task, though a smaller game board is nicer
greedapp(12, 29)
Nim
This is a translation of the Julia solution with some modifications. The most important is the way the new position is computed after choosing a direction. Rather than moving to the neighbor tile, we move to the farthest tile in the chosen direction as shown in the video.
There is of course a lot of differences due to the way gintro
library works which is quite different of Julia GTK API.
And we also added a score field to the BState object in order to restore the correct score when undoing a move.
import random, strutils
import gintro/[gobject, glib, gtk, gio]
const Rows = 22
const Cols = 79
type
Board = array[Rows, array[Cols, int]]
BState = object
board: Board
score: Natural
row, col: int
Game = ref object
window: Window
score: Natural
won: string
board: Board
buttons: array[Rows, array[Cols, Button]]
myRow, myCol: Natural
pastBoardStates: seq[BState]
proc update(game: Game) =
## Update game state.
var won = true
for i in 0..<Rows:
for j in 0..<Cols:
let val = game.board[i][j]
won = won and val == 0
game.buttons[i][j].setLabel(if val > 0: $val else: "")
game.buttons[game.myRow][game.myCol].setLabel("@")
game.won = if won: "WINNING " else: ""
game.window.setTitle(game.won & "Greed game (score: $#)".format(game.score))
proc eraseFromTile(game: Game; moveRow, moveCol: Natural) =
## Erase tile values from the given tile.
let xdir = moveRow - game.myRow
let ydir = moveCol - game.myCol
if abs(xdir) > 1 or abs(ydir) > 1 or xdir == 0 and ydir == 0 or game.board[moveRow][moveCol] == 0:
return
game.pastBoardStates.add BState(board: game.board, score: game.score,
row: game.myRow, col: game.myCol)
let count = game.board[moveRow][moveCol]
var x: int = game.myRow
var y: int = game.myCol
for i in 1..count:
inc x, xdir
inc y, ydir
if x in 0..<Rows and y in 0..<Cols:
game.board[x][y] = 0
inc game.score
else:
# Restore to last valid position.
dec x, xdir
dec y, ydir
game.board[game.myRow][game.myCol] = 0
game.myRow = x
game.myCol = y
game.update()
proc clicked(b: Button; game: Game) =
## Callback tp process a click in the grid.
var row, col = -1
for i in 0..<Rows:
for j in 0..<Cols:
if game.buttons[i][j] == b:
(row, col) = (i, j)
break
if row < 0: return
game.eraseFromTile(row, col)
proc undo(b: ToolButton; game: Game) =
## Callback to undo last move.
if game.won.len == 0 and game.pastBoardStates.len > 0:
let bst = game.pastBoardStates.pop()
game.board = bst.board
game.score = bst.score
game.myRow = bst.row
game.myCol = bst.col
game.update()
proc initialize(game: Game) =
## Initialize the game.
const PossibleVals = {1..9}
game.score = 0
game.won = ""
for i in 0..<Rows:
for j in 0..<Cols:
game.board[i][j] = PossibleVals.sample()
game.buttons[i][j].connect("clicked", clicked, game)
game.myRow = rand(Rows - 1)
game.myCol = rand(Cols - 1)
game.update()
proc initGame(b: ToolButton; game: Game) =
## Callback to start a new game.
game.initialize()
proc activate(app: Application) =
## Activate the application.
let game = new(Game)
let window = app.newApplicationWindow()
window.setSizeRequest(1600, 800)
window.setTitle("Greed game")
game.window = window
let box = newBox(Orientation.vertical, 0)
window.add box
let toolbar = newToolbar()
box.add toolbar
let newGame = newToolButton(label = "New game")
let undoMove = newToolButton(label = "Undo move")
toolbar.add(newGame)
toolbar.add(undoMove)
let srcWindow = newScrolledWindow()
box.packEnd(srcWindow, true, true, 0)
let grid = newGrid()
srcWindow.add grid
for i in 0..<Rows:
for j in 0..<Cols:
let b = newButton()
game.buttons[i][j] = b
grid.attach(b, j, i, 1, 1)
game.initialize()
newGame.connect("clicked", initGame, game)
undoMove.connect("clicked", undo, game)
game.window.showAll()
randomize()
let app = newApplication(Application, "Rosetta.greed")
discard app.connect("activate", activate)
discard app.run()
Perl
use strict;
use warnings;
use feature 'say';
my @board;
my $w = 79;
my $h = 22;
for (1..$h) {
my @row;
push @row, int 1 + rand 9 for 1..$w;
push @board, [@row];
}
my $X = int 0.5 + rand $w;
my $Y = int 0.5 + rand $h;
$board[$Y][$X] = '@';
my $score = 0;
sub execute {
my($y,$x) = @_;
my $i = $board[$Y+$y][$X+$x];
if (countSteps($i, $x, $y)) {
$score += $i;
$board[ $Y + $y*$_ ][ $X + $x*$_ ] = ' ' for 0..$i;
$board[ $Y += $y*$i ][ $X += $x*$i ] = '@';
}
}
sub countSteps {
my($i, $x, $y) = @_;
my $tX = $X;
my $tY = $Y;
for (0..$i) {
$tX += $x;
$tY += $y;
return 0 if $tX < 0 or $tY < 0 or $tX >= $w or $tY >= $h or $board[$tY][$tX] eq ' '
}
return 1
}
sub existsMoves {
for ([-1,-1], [-1,0], [-1,1], [0,-1], [0,0], [0,1], [1,-1], [1,0], [1,1]) {
my($x,$y) = @$_;
next if $x == 0 and $y == 0;
next if $X+$x < 0 or $X+$x > $w or $Y+$y < 0 or $Y+$y > $h ;
my $i = $board[$Y+$y][$X+$x];
return 1 if ( $i ne ' ' and countSteps($i, $x, $y) )
}
return 0;
}
while () {
say join '', @$_ for @board;
say "Game over." and last unless existsMoves();
print "Current score : " . $score . "\n";
my $c = <> ; chomp $c;
if ($c eq 'q') { say "So long." and last}
if ($c eq 'e') { execute(-1,-1) if $X > 0 and $Y > 0 } # North-West
if ($c eq 'r') { execute(-1, 0) if $Y > 0 } # North
if ($c eq 't') { execute(-1, 1) if $X < $w and $Y > 0 } # North-East
if ($c eq 'd') { execute( 0,-1) if $X > 0 } # West
if ($c eq 'g') { execute( 0, 1) if $X < $w } # East
if ($c eq 'x') { execute( 1,-1) if $X > 0 and $Y < $h } # South-West
if ($c eq 'c') { execute( 1, 0) if $Y < $h } # South
if ($c eq 'v') { execute( 1, 1) if $X < $w and $Y < $h } # South-East
}
Phix
constant W = 79, H = 22, NCOUNT = W*H
sequence board
integer X, Y, score
procedure printScore()
position(25,1); bk_color(2); text_color(10)
printf(1," SCORE: %d : %f%% ",{score,score*100/NCOUNT});
end procedure
procedure createBoard()
board = repeat(repeat('0',W),H)
for y=1 to H do
for x=1 to W do
board[y,x] = '0'+rand(9)
end for
end for
X = rand(W); Y = rand(H);
board[Y,X] = '0'; score = 0;
printScore();
end procedure
procedure displayBoard()
position(1,1)
bk_color(2)
for y=1 to H do
for x=1 to W do
integer ch = board[y,x];
text_color(iff(ch=' '?6:6+ch-'0'))
puts(1,ch)
end for
puts(1,"\n")
end for
bk_color(4); text_color(15); position(Y,X); puts(1,"@")
end procedure
function countSteps(integer i, x, y)
integer tX = X, tY = Y
while i do
i -= 1; tX += x; tY += y;
if tX<1 or tY<1 or tX>W or tY>H or board[tY,tX]=' ' then return false end if
end while
return true;
end function
procedure execute(integer x, y)
integer ch = board[Y+y,X+x],
i = iff(ch=' '?0:ch-'0')
if countSteps(i, x, y) then
score += i
while i do
i -= 1; X += x; Y += y;
board[Y,X] = ' ';
end while
end if
end procedure
procedure getInput()
while true do
integer k = upper(wait_key())
if k='Q' and X > 1 and Y > 1 then execute(-1,-1) exit
elsif k='W' and Y > 1 then execute( 0,-1) exit
elsif k='E' and X < W and Y > 1 then execute( 1,-1) exit
elsif k='A' and X > 1 then execute(-1, 0) exit
elsif k='D' and X < W then execute( 1, 0) exit
elsif k='Z' and X > 1 and Y < H then execute(-1, 1) exit
elsif k='X' and Y < H then execute( 0, 1) exit
elsif k='C' and X < W and Y < H then execute( 1, 1) exit
end if
end while
printScore();
end procedure
function existsMoves()
for y=-1 to +1 do
for x=-1 to +1 do
if (x or y)
and X+x>=1 and X+x<=W
and Y+y>=1 and Y+y<=H then
integer ch = board[Y+y,X+x];
if ch!=' ' and countSteps(ch-'0', x, y) then
return true
end if
end if
end for
end for
return false;
end function
procedure play()
while true do
cursor(NO_CURSOR); createBoard();
while true do
displayBoard(); getInput()
if not existsMoves() then exit end if
end while
displayBoard(); text_color(7);
position( 8,19); puts(1,"+----------------------------------------+");
position( 9,19); puts(1,"| GAME OVER |");
position(10,19); puts(1,"| PLAY AGAIN(Y/N)? |");
position(11,19); puts(1,"+----------------------------------------+");
position(10,48); cursor(BLOCK_CURSOR);
if upper(wait_key())!='Y' then return end if
end while
end procedure
play()
PicoLisp
Computer play by selecting random road. Demo is here.
(load "@lib/simul.l")
(seed (in "/dev/urandom" (rd 8)))
(scl 6)
# N - number
# C - Color
# F - flag to draw candidates
# A - @ mark
(de display ()
(let P 0
(wait 500)
(prin "^[[2J")
(for L G
(for This L
# count cleared cells
(and (lt0 (: N)) (inc 'P))
(prin
"^[[0;"
(if (or (: A) (: F)) 100 (: C))
"m"
(cond
((: A) "@")
((lt0 (: N)) " ")
(T (: N)) )
"^[[0m" ) )
(prinl) )
(prinl
"Score: "
S
" "
(round (*/ P 1.0 100.0 1738.0) 2)
"%" ) ) )
(de roads (Lst Flg)
(mapc
'((L)
(with C
(do (car L)
(setq This ((cadr L) This))
(=: F Flg) ) ) )
Lst )
(display) )
(let
(Colors (simul~shuffle (31 32 33 35 91 92 93 94 96))
G (simul~grid 22 79)
C NIL
S 0 )
# set random grid
(for L G
(for This L
(let X (rand 1 9)
(=: N X)
(=: C (get Colors X)) ) ) )
# set random startpoint
(with (get G (rand 1 22) (rand 1 79))
(setq C This)
(=: A 0) )
(display)
(loop
(NIL
(setq Z
(extract
'((D)
(with C
(let? S (with (D This) (: N))
(and
(do S
(NIL (setq This (D This)))
(NIL (gt0 (: N)))
'next )
(list S D This) ) ) ) )
'(simul~west
simul~east
simul~south
simul~north
((X) (simul~south (simul~west X)))
((X) (simul~north (simul~west X)))
((X) (simul~south (simul~east X)))
((X) (simul~north (simul~east X))) ) ) ) )
# XXX
(roads Z T)
(roads Z)
# select road randomly
(let L (get Z (rand 1 (length Z)))
(with C
(inc 'S (: N))
# clear value of "old" Center
(=: N -1)
(=: A)
# clear selected road
(do (car L)
(inc 'S (: N))
(=: N -1)
(setq This ((cadr L) This)) ) )
# set new Center
(with (caddr L)
(setq C This)
(=: A 0) ) )
(display) ) )
(bye)
Raku
# 20200913 added Raku programming solution
srand 123456;
my @board = [ (1..9).roll xx my \w = 79 ] xx my \h = 22 ;
my \X = $ = w.rand.Int ; my \Y = $ = h.rand.Int;
@board[Y;X] = '@';
my \score = $ = 0;
sub execute (\y,\x) {
my \i = $ = @board[Y+y;X+x];
if countSteps(i, x, y) {
score += i;
@board[ Y + y*$_ ; X + x*$_ ] = ' ' for ^i;
@board[ Y += y*i ; X += x*i ] = '@';
}
}
sub countSteps(\i, \x, \y) {
my \tX = $ = X ; my \tY = $ = Y;
for ^i {
tX += x; tY += y;
return False if tX < 0 or tY < 0 or tX ≥ w or tY ≥ h or @board[tY;tX] eq ' '
}
return True;
}
sub existsMoves {
for (-1 .. 1) X (-1 .. 1) -> (\x,\y) {
next if x == 0 and y == 0;
next if X+x < 0 or X+x > w or Y+y < 0 or Y+y > h ;
my \i = @board[Y+y;X+x];
return True if ( i ne ' ' and countSteps(i, x, y) )
}
return False;
}
loop {
for @board { .join.print ; print "\r\n" } ;
{ say "Game over." and last } unless existsMoves();
print "Current score : ", score, "\r\n";
given my $c = $*IN.getc {
when 'q' { say "So long." and last}
when 'e' { execute(-1,-1) if X > 0 and Y > 0 } # North-West
when 'r' { execute(-1, 0) if Y > 0 } # North
when 't' { execute(-1, 1) if X < w and Y > 0 } # North-East
when 'd' { execute( 0,-1) if X > 0 } # West
when 'g' { execute( 0, 1) if X < w } # East
when 'x' { execute( 1,-1) if X > 0 and Y < h } # South-West
when 'c' { execute( 1, 0) if Y < h } # South
when 'v' { execute( 1, 1) if X < w and Y < h } # South-East
}
}
REXX
This REXX version's only dependency is that the DOS command cls is used to clear the terminal screen.
No attempt was made to validate the input the input arguments (parameters) for this REXX program.
Pointers (above and to the right of) the grid are included to help identify where the current location is.
/*REXX program lets a user play the game of GREED (by Matthew Day) from the console. */
parse arg sw sd @ b ?r . /*obtain optional argumenst from the CL*/
if sw=='' | sw=="," then sw= 79 /*cols specified? Then use the default*/
if sd=='' | sd=="," then sd= 22 /*rows " " " " " */
if @=='' | @=="," then @= '@' /*here " " " " " */
if b=='' | b=="," then b= ' ' /*blank " " " " " */
if datatype(?r, 'W') then call random ,,?r /*maybe use a seed for the RANDOM BIF*/
if length(@)==2 & datatype(@,'X') then @=x2c(@) /*maybe use @ char for current pos. */
if length(b)==2 & datatype(b,'X') then b=x2c(b) /* " " B char for background. */
signal on halt /*handle pressing of Ctrl-Break key. */
call init /* [↓] CLR is reset if there's an err*/
clr=1; do until # == sw*sd; ??= /*keep playing until the grid is blank.*/
call show clr /*show the playing field (grid) to term*/
call ask; clr= 1 /*obtain user's move, validate, or quit*/
if \move() then do; clr= 0 /*perform the user's move per @ loc.*/
if ??==@. then say ____ "invalid move: moving out of bounds."
if ??==b then say ____ "invalid move: moving into a blank."
end
call show 0
end /*until*/ /* [↑] Also, if out─of─bounds, LEAVE. */
if show(1)==sw*sd then say ____ "You've won, the grid is blank, your score is: " #
exit 2
exit 0 /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
ask: do forever /*play 'til done, or no possible moves.*/
say ____ 'moves:' ____ ' Q= ◄↑ W= ↑ E= ►↑'
say ____ 'moves:' ____ ' A= ◄ D= ►'
say ____ 'moves:' ____ ' Z= ◄↓ X= ↓ C= ►↓'
say ____
say ____ 'enter a move ──or── QUIT (the score is: ' #")"
parse pull z 2 1 what . 1 oz; upper z what
if abbrev('QUIT', what, 2) | abbrev("QQUIT", what, 2) then leave
if length( space(oz) )==1 & pos(z, 'QWEADZXC')\==0 then return
say ____ '***error*** invalid direction for a move:' space(oz); say
end /*forever*/
halt: say; say ____ 'quitting.'; exit 1
/*──────────────────────────────────────────────────────────────────────────────────────*/
init: @.= 'ff'x; $.=.; ____= copies("─", 8) /*out─of─bounds literal; fence for SAYs*/
do r=1 for sd
do c=1 for sw; @.r.c= random(1, 9) /*assign grid area to random digs (1►9)*/
end /*c*/
end /*r*/
!r= random(1, sd); !c= random(1, sw); @.!r.!c= @; return /*assign 1st position*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
move: @.!r.!c= '≤'; $r= !r; $c= !c; ??= /*blank out this "start" position. */
@@=. /*nullify the count of move positions. */
do until @@==0; select
when z== 'Q' then do; !r= !r - 1; !c= !c - 1; end
when z== 'W' then !r= !r - 1
when z== 'E' then do; !r= !r - 1; !c= !c + 1; end
when z== 'A' then !c= !c - 1
when z== 'D' then !c= !c + 1
when z== 'Z' then do; !r= !r + 1; !c= !c - 1; end
when z== 'X' then !r= !r + 1
when z== 'C' then do; !r= !r + 1; !c= !c + 1; end
end /*select*/
?= @.!r.!c; if ?==@. | ?==b then do; !r= $r; !c= $c; ??= ?; return 0
end
if @@==. then @@=?; if datatype(@@, 'W') then @@= @@ - 1 /*diminish cnt.*/
@.!r.!c= '±' /*nullify (later, a blank) position. */
end /*until*/
@.!r.!c= @; return 1 /*signify current grid position with @ */
/*──────────────────────────────────────────────────────────────────────────────────────*/
show: arg tell; #=0; if tell then do; "CLS"; say left('', !c)"↓"; end
do r=1 for sd; _= ' ' /* [↑] DOS cmd CLS clears the screen*/
do c=1 for sw /*construct row of the grid for display*/
if @.r.c=="±" & ??\=='' then @.r.c= $.r.c /*Is this a temp blank? Restore*/
if @.r.c=="±" & ?? =='' then @.r.c= b /*Is this a temp blank? Blank. */
if @.r.c=="≤" & ??\=='' then @.r.c= $.r.c /*Is this a temp a @ ? Restore*/
if @.r.c=="≤" & ?? =='' then @.r.c= b /*Is this a temp a @ ? Blank. */
?= @.r.c; _= _ || ? /*construct a line of the grid for term*/
if ?==b | ?==@ then #= # + 1 /*Position==b ─or─ @? Then bump score.*/
if tell then $.r.c= @.r.c /*create a backup grid for re─instating*/
end /*c*/
if r==!r then _= _ '◄' /*indicate row of current position. */
if tell then say _ /*display a row of grid to screen. */
end /*r*/; say; return # /*SHOW also counts # of blanks (score).*/
A note on the OUTPUT sections: each (cleared) screen displayed is shown below as a separate OUTPUT section.
The following are the screen shots when inputs used (size of the grid) are: 22 10
- output : the 1st screen shown.
↓ 1636166561333644938615925878672969839136949348125385742112849651343354296271245 4935939188413836477495369151362748736256329449564639583731265554747438655579797 2761827294343258918258167935625127433626644177165772453435474591949917695547965 5336784646373682676398688475989972451499776252164989899239191733912697265898925 4952948995581413589577455233495962736898536553933712711747529619371573895413265 5328643745672485468516645326176482571162377128958669252244431799914145324756787 9682648416475828434376154259111596818112819626518754715385939211764235211148126 4771918124154627513339665771138169237888886368882335865655526894655352121961215 794644718989445262471866768299551827168758297323537929749@815519895387457566428 ◄ 9347969832617624113866732722842121521854745888458198852913265875445986923272597 ──────── moves: ──────── Q= ◄↑ W= ↑ E= ►↑ ──────── moves: ──────── A= ◄ D= ► ──────── moves: ──────── Z= ◄↓ X= ↓ C= ►↓ ──────── ──────── enter a move ──or── enter QUIT to quit. (score is: 1) e ◄■■■■■■■■■■■■■ user input
- output : the 2nd screen shown.
↓ 1636166561333644938615925878672969839136949348125385742112849651343354296271245 4935939188413836477495369151362748736256329449564639583731265554747438655579797 2761827294343258918258167935625127433626644177165772453435474591949917695547965 53367846463736826763986884759899724514997762521649898992391917@3912697265898925 ◄ 4952948995581413589577455233495962736898536553933712711747529 19371573895413265 532864374567248546851664532617648257116237712895866925224443 799914145324756787 96826484164758284343761542591115968181128196265187547153859 9211764235211148126 4771918124154627513339665771138169237888886368882335865655 26894655352121961215 794644718989445262471866768299551827168758297323537929749 815519895387457566428 9347969832617624113866732722842121521854745888458198852913265875445986923272597 ──────── moves: ──────── Q= ◄↑ W= ↑ E= ►↑ ──────── moves: ──────── A= ◄ D= ► ──────── moves: ──────── Z= ◄↓ X= ↓ C= ►↓ ──────── ──────── enter a move ──or── enter QUIT to quit. (score is: 6) quit ◄■■■■■■■■■■■■■ user input ──────── quitting.
Wren
See Greed/Wren.
Z80 Assembly
See Greed/Z80 Assembly.