asciiplanes.sf 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #!/usr/bin/ruby
  2. #`(if running under some shell) {
  3. eval 'exec /usr/bin/sidef $0 ${1+"$@"}'
  4. }
  5. # Author: Daniel "Trizen" Șuteu
  6. # License: GPLv3
  7. # Created on: 21 August 2012
  8. # Latest edit on: 10 November 2013
  9. # Translated to Sidef in 21 September 2014
  10. # Latest edit on: 16 November 2022
  11. # Website: https://github.com/trizen/asciiplanes
  12. # Find the planes' positions on a grid. (text-based game)
  13. var asciitable =
  14. try { require('Text::ASCIITable') }
  15. catch { STDERR.print("Can't load the 'Text::ASCIITable' Perl module...\n"); Sys.exit(2) }
  16. var ANSI =
  17. try { frequire('Term::ANSIColor') }
  18. catch { nil }
  19. ## Package variables
  20. var pkgname = 'asciiplanes'
  21. var version = 0.06
  22. ## Game variables
  23. var BOARD_SIZE = 8
  24. var PLANES_NUM = 3
  25. var parts = (['head'] + ['hit']*7)
  26. var plane_chars = ['$', '#', '@']
  27. var use_colors = defined(ANSI)
  28. var wrap_plane = false
  29. var hit_char = %q{O}
  30. var miss_char = %q{`}
  31. func print_usage {
  32. print <<"EOT"
  33. usage: #{__MAIN__} [options]
  34. main:
  35. --size=i : length side of the board (default: #{BOARD_SIZE})
  36. --planes=i : the total number of planes (default: #{PLANES_NUM})
  37. --wrap! : wrap the plane around the play board (default: #{wrap_plane})
  38. --hit=s : character used when a plane is hit (default: "#{hit_char}")
  39. --miss=s : character used when a plane is missed (default: "#{miss_char}")
  40. --colors! : use ANSI colors (requires Term::ANSIColor) (default: #{use_colors})
  41. help:
  42. --help : print this message and exit
  43. --version : print the version number and exit
  44. example:
  45. #{__MAIN__} --size=12 --planes=6 --hit='*'
  46. EOT
  47. Sys.exit
  48. }
  49. func print_version {
  50. print "#{pkgname} #{version}\n"
  51. Sys.exit
  52. }
  53. if (ARGV) {
  54. ARGV.getopt!(
  55. 'board-size|size=i' => \BOARD_SIZE,
  56. 'planes-num=i' => \PLANES_NUM,
  57. 'hit-char=s' => \hit_char,
  58. 'miss-char=s' => \miss_char,
  59. 'wrap!' => \wrap_plane,
  60. 'colors!' => \use_colors,
  61. 'help|h|?' => print_usage,
  62. 'version|v|V' => print_version,
  63. )
  64. }
  65. ## The play-board of the game, and some other arrays
  66. #---------------------------------------------------------------
  67. var play_board = BOARD_SIZE.of { [nil] * BOARD_SIZE }
  68. var info_board = BOARD_SIZE.of { [' '] * BOARD_SIZE }
  69. var letters = Hash()
  70. play_board.range.each { |i|
  71. static char = 'a'
  72. letters{char++} = i
  73. }
  74. #---------------------------------------------------------------
  75. func pointers(board, x, y, indices) {
  76. gather {
  77. [[0,0]] + indices -> each { |pair|
  78. pair.kind_of(Array) || next
  79. var (row, col) = (x + pair[0], y + pair[1])
  80. if (wrap_plane) {
  81. row %= BOARD_SIZE #=
  82. col %= BOARD_SIZE #=
  83. }
  84. row.is_between(0, BOARD_SIZE-1) || return []
  85. col.is_between(0, BOARD_SIZE-1) || return []
  86. take(\board[row][col])
  87. }
  88. }
  89. }
  90. func up(board, x, y) {
  91. pointers(board, x, y, [
  92. '[+0, +0]',
  93. [+1, -1], [+1, +0], [+1, +1],
  94. [+2, +0],
  95. [+3, -1], [+3, +0], [+3, +1],
  96. ])
  97. }
  98. func down(board, x, y) {
  99. pointers(board, x, y, [
  100. [-3, -1], [-3, +0], [-3, +1],
  101. [-2, +0],
  102. [-1, -1], [-1, +0], [-1, +1],
  103. '[+0, +0]',
  104. ])
  105. }
  106. func left(board, x, y) {
  107. pointers(board, x, y, [
  108. [-1, +1], [-1, +3],
  109. '[+0, +0]', [+0, +1], [+0, +2], [+0, +3],
  110. [+1, +1], [+1, +3],
  111. ])
  112. }
  113. func right(board, x, y) {
  114. pointers(board, x, y, [
  115. [-1, -3], [-1, -1],
  116. [+0, -3], [+0, -2], [+0, -1], '[+0, +0]',
  117. [+1, -3], [+1, -1],
  118. ])
  119. }
  120. func assign(change=false, plane=[], data=[]) {
  121. plane.len || return false
  122. change || (
  123. plane.each { |c|
  124. *c == nil || return false
  125. }
  126. )
  127. plane.range.each { |i|
  128. *plane[i] = data[i]
  129. }
  130. return true
  131. }
  132. func print_ascii_table {
  133. var table = asciitable.new(Hash(headingText => "#{pkgname} #{version}"))
  134. table.setCols(' ', (1..BOARD_SIZE)...)
  135. var char = 'a';
  136. info_board.each { |row|
  137. table.addRow([char++, row...])
  138. table.addRowLine()
  139. }
  140. var t = table.drawit
  141. if (defined(ANSI) && use_colors) {
  142. t.gsub!(hit_char, ANSI.colored(hit_char, 'bold red'))
  143. t.gsub!(miss_char, ANSI.colored(miss_char, 'yellow'))
  144. plane_chars.each {|c|
  145. t.gsub!(c, ANSI.colored(c, "bold green"))
  146. }
  147. }
  148. say t
  149. }
  150. var count = 0
  151. var max_tries = 1_000
  152. var directions = [up, down, left, right]
  153. while (count != PLANES_NUM) {
  154. var x = play_board.end.irand
  155. var y = play_board[0].end.irand
  156. var rand = directions.end.irand
  157. var code = directions[rand]
  158. if (--max_tries <= 0) {
  159. die "FATAL ERROR: try to increase the size of the grid (--size=x).\n"
  160. }
  161. assign(
  162. change: false,
  163. plane: code.call(play_board, x, y),
  164. data: parts.map {|c| "#{c}_#{rand}" },
  165. ) || next
  166. count++
  167. }
  168. ## MAIN
  169. var tries = 0
  170. var start_time = Time.new.sec
  171. print_ascii_table()
  172. while (count > 0) {
  173. var letter = letters.keys.rand
  174. var number = irand(1, BOARD_SIZE)
  175. say "=>> Your guess (e.g.: #{letter}#{number})"
  176. var input = (Sys.scanln("> ") \\ break -> lc)
  177. input ~~ ['q', 'quit'] && break
  178. var m = input.match(/^\h*([a-z]+)\D*([0-9]+)/) || next
  179. letters.has_key(m[0]) || next
  180. var x = letters{m[0]}
  181. var y = Num(m[1]).dec
  182. (y >= 0) && (y < BOARD_SIZE) || next
  183. var point = play_board[x][y]
  184. if (point == nil) {
  185. info_board[x][y] = miss_char
  186. }
  187. elsif (point.match(/^head_(\d)$/i)) { |m|
  188. var dir = Num(m[0])
  189. var item = plane_chars[(PLANES_NUM - count) % plane_chars.len]
  190. var code = directions[dir]
  191. [play_board, info_board].each { |board|
  192. assign(
  193. change: true,
  194. data: [item]*8,
  195. plane: code.call(board, x, y),
  196. ) || die "#{__MAIN__}: unexpected error!"
  197. }
  198. count--;
  199. }
  200. elsif (point ~~ /^hit_\d$/i) {
  201. info_board[x][y] = hit_char
  202. }
  203. tries++
  204. print_ascii_table()
  205. }
  206. printf("** Info: %d tries in %d seconds\n", tries, Time.new.sec - start_time)
  207. if (count == 0) {
  208. say "** Congratulations! All the planes are destroyed!"
  209. }