snake_game_oo.sf 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #!/usr/bin/ruby
  2. #
  3. ## https://rosettacode.org/wiki/Snake
  4. #
  5. class SnakeGame(w, h) {
  6. const readkey = frequire('Term::ReadKey')
  7. const ansi = frequire('Term::ANSIColor')
  8. enum (VOID, HEAD, BODY, TAIL, FOOD)
  9. define (
  10. LEFT = [+0, -1],
  11. RIGHT = [+0, +1],
  12. UP = [-1, +0],
  13. DOWN = [+1, +0],
  14. )
  15. define (
  16. BG_COLOR = "on_black"
  17. FOOD_COLOR = ("red" + " " + BG_COLOR)
  18. SNAKE_COLOR = ("bold green" + " " + BG_COLOR)
  19. SLEEP_SEC = 0.02
  20. )
  21. const (
  22. A_VOID = ansi.colored(' ', BG_COLOR),
  23. A_FOOD = ansi.colored('❇', FOOD_COLOR),
  24. A_BLOCK = ansi.colored('■', SNAKE_COLOR),
  25. )
  26. has dir = LEFT
  27. has grid = [[]]
  28. has head_pos = [0, 0]
  29. has tail_pos = [0, 0]
  30. method init {
  31. grid = h.of { w.of { [VOID] } }
  32. head_pos = [h>>1, w>>1]
  33. tail_pos = [head_pos[0], head_pos[1]+1]
  34. grid[head_pos[0]][head_pos[1]] = [HEAD, dir] # head
  35. grid[tail_pos[0]][tail_pos[1]] = [TAIL, dir] # tail
  36. self.make_food()
  37. }
  38. method make_food {
  39. var (food_x, food_y)
  40. do {
  41. food_x = w.rand.int
  42. food_y = h.rand.int
  43. } while (grid[food_y][food_x][0] != VOID)
  44. grid[food_y][food_x][0] = FOOD
  45. }
  46. method display {
  47. print("\033[H", grid.map { |row|
  48. row.map { |cell|
  49. given (cell[0]) {
  50. when (VOID) { A_VOID }
  51. when (FOOD) { A_FOOD }
  52. default { A_BLOCK }
  53. }
  54. }.join('')
  55. }.join("\n")
  56. )
  57. }
  58. method move {
  59. var grew = false
  60. # Move the head
  61. var (y, x) = head_pos...
  62. var new_y = (y+dir[0] % h)
  63. var new_x = (x+dir[1] % w)
  64. var cell = grid[new_y][new_x]
  65. given (cell[0]) {
  66. when (BODY) { die "\nYou just bit your own body!\n" }
  67. when (TAIL) { die "\nYou just bit your own tail!\n" }
  68. when (FOOD) { grew = true; self.make_food() }
  69. }
  70. # Create a new head
  71. grid[new_y][new_x] = [HEAD, dir]
  72. # Replace the current head with body
  73. grid[y][x] = [BODY, dir]
  74. # Update the head position
  75. head_pos = [new_y, new_x]
  76. # Move the tail
  77. if (!grew) {
  78. var (y, x) = tail_pos...
  79. var pos = grid[y][x][1]
  80. var new_y = (y+pos[0] % h)
  81. var new_x = (x+pos[1] % w)
  82. grid[y][x][0] = VOID # erase the current tail
  83. grid[new_y][new_x][0] = TAIL # create a new tail
  84. tail_pos = [new_y, new_x]
  85. }
  86. }
  87. method play {
  88. STDOUT.autoflush(true)
  89. readkey.ReadMode(3)
  90. try {
  91. loop {
  92. var key
  93. while (!defined(key = readkey.ReadLine(-1))) {
  94. self.move()
  95. self.display()
  96. Sys.sleep(SLEEP_SEC)
  97. }
  98. given (key) {
  99. when ("\e[A") { if (dir != DOWN ) { dir = UP } }
  100. when ("\e[B") { if (dir != UP ) { dir = DOWN } }
  101. when ("\e[C") { if (dir != LEFT ) { dir = RIGHT } }
  102. when ("\e[D") { if (dir != RIGHT) { dir = LEFT } }
  103. }
  104. }
  105. }
  106. catch {
  107. readkey.ReadMode(0)
  108. }
  109. }
  110. }
  111. var w = Number(`tput cols` || 80)
  112. var h = Number(`tput lines` || 24)
  113. SnakeGame(w || 80, h || 24).play