#!/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: 26 July 2016
# 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) }
);
## Package variables
var pkgname = 'asciiplanes';
var version = 0.05;
## Game variables
var BOARD_SIZE = 8;
var PLANES_NUM = 3;
var parts = (['head'] + ['hit']*7);
var plane_chars = ['$', '#', '@'];
var wrap_plane = false;
var hit_char = %q{*};
var miss_char = %q{`};
func 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}")
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 ver {
print "#{pkgname} #{version}\n";
Sys.exit;
}
if (!ARGV.is_empty) {
var getopt = (
try { frequire('Getopt::Long') }
catch { STDERR.print("Can't load the 'Getopt::Long' Perl module...\n"); Sys.exit(2) }
);
getopt.GetOptions(
'board-size|size=i' => func(_,v) { BOARD_SIZE = Num(v) },
'planes-num=i' => func(_,v) { PLANES_NUM = Num(v) },
'hit-char=s' => func(_,v) { hit_char = Str(v) },
'miss-char=s' => func(_,v) { miss_char = Str(v) },
'wrap!' => func(_,v) { wrap_plane = Bool(v) },
'help|h|?' => func(*_) { usage() },
'version|v|V' => func(*_) { ver() },
);
}
## 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.new;
play_board.range.each { |i|
static char = 'a';
letters{char++} = i;
}
#---------------------------------------------------------------
func pointers(board, x, y, indices) {
var output = [];
[[0,0]] + indices -> each { |pair|
pair.is_an(Array) || next;
var (row, col) = (x + pair[0], y + pair[1]);
if (row > board.end) {
wrap_plane || return [];
row %= board.len;
}
if (col > board[row].end) {
wrap_plane || return [];
col %= board[row].len;
}
if (!wrap_plane && (row<0 || col<0)) {
return [];
}
output.append(\board[row][col]);
}
return output;
}
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 == 0 ->
&& 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();
}
say table.drawit;
}
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);
var text = "=>> Your guess (e.g.: #{letter}#{number})\n> ";
var input = lc(Sys.scanln(text) \\ break);
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 = m[1].to_num.dec;
(y >= 0) && (y < BOARD_SIZE) || next;
var point = play_board[x][y];
if (point == nil) {
info_board[x][y] = miss_char;
}
elsif (var m = point.match(/^head_(\d)$/i)) {
var dir = m[0].to_num;
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!";
}