#!/usr/bin/ruby
#`(if running under some shell) {
eval 'exec /usr/bin/sidef $0 ${1+"$@"}'
}
# Author: Daniel "Trizen" Șuteu
# License: GPLv3
# Created on: 21 August 2012
# Latest edit on: 10 November 2013
# Translated to Sidef in 21 September 2014
# Latest edit on: 16 November 2022
# Website: https://github.com/trizen/asciiplanes
# Find the planes' positions on a grid. (text-based game)
var asciitable =
try { require('Text::ASCIITable') }
catch { STDERR.print("Can't load the 'Text::ASCIITable' Perl module...\n"); Sys.exit(2) }
var ANSI =
try { frequire('Term::ANSIColor') }
catch { nil }
## Package variables
var pkgname = 'asciiplanes'
var version = 0.06
## Game variables
var BOARD_SIZE = 8
var PLANES_NUM = 3
var parts = (['head'] + ['hit']*7)
var plane_chars = ['$', '#', '@']
var use_colors = defined(ANSI)
var wrap_plane = false
var hit_char = %q{O}
var miss_char = %q{`}
func print_usage {
print <<"EOT"
usage: #{__MAIN__} [options]
main:
--size=i : length side of the board (default: #{BOARD_SIZE})
--planes=i : the total number of planes (default: #{PLANES_NUM})
--wrap! : wrap the plane around the play board (default: #{wrap_plane})
--hit=s : character used when a plane is hit (default: "#{hit_char}")
--miss=s : character used when a plane is missed (default: "#{miss_char}")
--colors! : use ANSI colors (requires Term::ANSIColor) (default: #{use_colors})
help:
--help : print this message and exit
--version : print the version number and exit
example:
#{__MAIN__} --size=12 --planes=6 --hit='*'
EOT
Sys.exit
}
func print_version {
print "#{pkgname} #{version}\n"
Sys.exit
}
if (ARGV) {
ARGV.getopt!(
'board-size|size=i' => \BOARD_SIZE,
'planes-num=i' => \PLANES_NUM,
'hit-char=s' => \hit_char,
'miss-char=s' => \miss_char,
'wrap!' => \wrap_plane,
'colors!' => \use_colors,
'help|h|?' => print_usage,
'version|v|V' => print_version,
)
}
## The play-board of the game, and some other arrays
#---------------------------------------------------------------
var play_board = BOARD_SIZE.of { [nil] * BOARD_SIZE }
var info_board = BOARD_SIZE.of { [' '] * BOARD_SIZE }
var letters = Hash()
play_board.range.each { |i|
static char = 'a'
letters{char++} = i
}
#---------------------------------------------------------------
func pointers(board, x, y, indices) {
gather {
[[0,0]] + indices -> each { |pair|
pair.kind_of(Array) || next
var (row, col) = (x + pair[0], y + pair[1])
if (wrap_plane) {
row %= BOARD_SIZE #=
col %= BOARD_SIZE #=
}
row.is_between(0, BOARD_SIZE-1) || return []
col.is_between(0, BOARD_SIZE-1) || return []
take(\board[row][col])
}
}
}
func up(board, x, y) {
pointers(board, x, y, [
'[+0, +0]',
[+1, -1], [+1, +0], [+1, +1],
[+2, +0],
[+3, -1], [+3, +0], [+3, +1],
])
}
func down(board, x, y) {
pointers(board, x, y, [
[-3, -1], [-3, +0], [-3, +1],
[-2, +0],
[-1, -1], [-1, +0], [-1, +1],
'[+0, +0]',
])
}
func left(board, x, y) {
pointers(board, x, y, [
[-1, +1], [-1, +3],
'[+0, +0]', [+0, +1], [+0, +2], [+0, +3],
[+1, +1], [+1, +3],
])
}
func right(board, x, y) {
pointers(board, x, y, [
[-1, -3], [-1, -1],
[+0, -3], [+0, -2], [+0, -1], '[+0, +0]',
[+1, -3], [+1, -1],
])
}
func assign(change=false, plane=[], data=[]) {
plane.len || return false
change || (
plane.each { |c|
*c == nil || return false
}
)
plane.range.each { |i|
*plane[i] = data[i]
}
return true
}
func print_ascii_table {
var table = asciitable.new(Hash(headingText => "#{pkgname} #{version}"))
table.setCols(' ', (1..BOARD_SIZE)...)
var char = 'a';
info_board.each { |row|
table.addRow([char++, row...])
table.addRowLine()
}
var t = table.drawit
if (defined(ANSI) && use_colors) {
t.gsub!(hit_char, ANSI.colored(hit_char, 'bold red'))
t.gsub!(miss_char, ANSI.colored(miss_char, 'yellow'))
plane_chars.each {|c|
t.gsub!(c, ANSI.colored(c, "bold green"))
}
}
say t
}
var count = 0
var max_tries = 1_000
var directions = [up, down, left, right]
while (count != PLANES_NUM) {
var x = play_board.end.irand
var y = play_board[0].end.irand
var rand = directions.end.irand
var code = directions[rand]
if (--max_tries <= 0) {
die "FATAL ERROR: try to increase the size of the grid (--size=x).\n"
}
assign(
change: false,
plane: code.call(play_board, x, y),
data: parts.map {|c| "#{c}_#{rand}" },
) || next
count++
}
## MAIN
var tries = 0
var start_time = Time.new.sec
print_ascii_table()
while (count > 0) {
var letter = letters.keys.rand
var number = irand(1, BOARD_SIZE)
say "=>> Your guess (e.g.: #{letter}#{number})"
var input = (Sys.scanln("> ") \\ break -> lc)
input ~~ ['q', 'quit'] && break
var m = input.match(/^\h*([a-z]+)\D*([0-9]+)/) || next
letters.has_key(m[0]) || next
var x = letters{m[0]}
var y = Num(m[1]).dec
(y >= 0) && (y < BOARD_SIZE) || next
var point = play_board[x][y]
if (point == nil) {
info_board[x][y] = miss_char
}
elsif (point.match(/^head_(\d)$/i)) { |m|
var dir = Num(m[0])
var item = plane_chars[(PLANES_NUM - count) % plane_chars.len]
var code = directions[dir]
[play_board, info_board].each { |board|
assign(
change: true,
data: [item]*8,
plane: code.call(board, x, y),
) || die "#{__MAIN__}: unexpected error!"
}
count--;
}
elsif (point ~~ /^hit_\d$/i) {
info_board[x][y] = hit_char
}
tries++
print_ascii_table()
}
printf("** Info: %d tries in %d seconds\n", tries, Time.new.sec - start_time)
if (count == 0) {
say "** Congratulations! All the planes are destroyed!"
}