snake_game.sf 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #!/usr/bin/ruby
  2. # Author: Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 25 June 2015
  5. # Website: https://github.com/trizen
  6. # The snake game. (with colors + Unicode)
  7. const readkey = frequire('Term::ReadKey');
  8. const ansi = frequire('Term::ANSIColor');
  9. enum(
  10. VOID,
  11. HEAD,
  12. BODY,
  13. TAIL,
  14. FOOD,
  15. );
  16. define (
  17. LEFT = [+0, -1],
  18. RIGHT = [+0, +1],
  19. UP = [-1, +0],
  20. DOWN = [+1, +0],
  21. );
  22. const BG_COLOR = "on_black";
  23. const FOOD_COLOR = ("red" + " " + BG_COLOR);
  24. const SNAKE_COLOR = ("bold green" + " " + BG_COLOR);
  25. const (
  26. U_HEAD = ansi.colored('▲', SNAKE_COLOR),
  27. D_HEAD = ansi.colored('▼', SNAKE_COLOR),
  28. L_HEAD = ansi.colored('◀', SNAKE_COLOR),
  29. R_HEAD = ansi.colored('▶', SNAKE_COLOR),
  30. );
  31. const (
  32. U_BODY = ansi.colored('╹', SNAKE_COLOR),
  33. D_BODY = ansi.colored('╻', SNAKE_COLOR),
  34. L_BODY = ansi.colored('╴', SNAKE_COLOR),
  35. R_BODY = ansi.colored('╶', SNAKE_COLOR),
  36. );
  37. const (
  38. U_TAIL = ansi.colored('╽', SNAKE_COLOR),
  39. D_TAIL = ansi.colored('╿', SNAKE_COLOR),
  40. L_TAIL = ansi.colored('╼', SNAKE_COLOR),
  41. R_TAIL = ansi.colored('╾', SNAKE_COLOR),
  42. );
  43. const A_VOID = ansi.colored(' ', BG_COLOR);
  44. const A_FOOD = ansi.colored('❇', FOOD_COLOR);
  45. var sleep = 0.02; # sleep duration between updates
  46. var food_num = 10; # number of initial food sources
  47. var w = `tput cols`.to_i;
  48. var h = `tput lines`.to_i;
  49. var r = "\033[H";
  50. var dir = LEFT;
  51. var grid = h.of { w.of { Array.new(VOID) } };
  52. var head_pos = [h>>1, w>>1];
  53. var tail_pos = [head_pos[0], head_pos[1]+1];
  54. grid[head_pos[0]][head_pos[1]] = [HEAD, dir]; # head
  55. grid[tail_pos[0]][tail_pos[1]] = [TAIL, dir]; # tail
  56. func make_food {
  57. var (food_x, food_y);
  58. do {
  59. food_x = w.rand.int;
  60. food_y = h.rand.int;
  61. } while (grid[food_y][food_x][0] != VOID);
  62. grid[food_y][food_x][0] = FOOD;
  63. }
  64. { make_food() } * food_num;
  65. func display {
  66. static i = 0;
  67. static s = [UP, DOWN, LEFT, RIGHT];
  68. print(r, grid.map { |row|
  69. row.map { |cell|
  70. if (cell[0] != VOID) {
  71. i = s.index(cell[1])
  72. }
  73. given (cell[0]) {
  74. when (VOID) { A_VOID }
  75. when (FOOD) { A_FOOD }
  76. when (BODY) { [U_BODY, D_BODY, L_BODY, R_BODY][i] }
  77. when (HEAD) { [U_HEAD, D_HEAD, L_HEAD, R_HEAD][i] }
  78. when (TAIL) { [U_TAIL, D_TAIL, L_TAIL, R_TAIL][i] }
  79. }
  80. }.join('')
  81. }.join("\n")
  82. );
  83. }
  84. func move {
  85. var grew = false;
  86. # Move the head
  87. var (y, x) = head_pos...;
  88. var new_y = (y+dir[0] % h);
  89. var new_x = (x+dir[1] % w);
  90. var cell = grid[new_y][new_x];
  91. given (cell[0]) {
  92. when (BODY) { die "\nYou just bit your own body!\n" }
  93. when (TAIL) { die "\nYou just bit your own tail!\n" }
  94. when (FOOD) { grew = true; make_food() }
  95. }
  96. # Create a new head
  97. grid[new_y][new_x] = [HEAD, dir];
  98. # Replace the current head with body
  99. grid[y][x] = [BODY, dir];
  100. # Update the head position
  101. head_pos = [new_y, new_x];
  102. # Move the tail
  103. if (!grew) {
  104. var (y, x) = tail_pos...;
  105. var pos = grid[y][x][1];
  106. var new_y = (y+pos[0] % h);
  107. var new_x = (x+pos[1] % w);
  108. grid[y][x][0] = VOID; # erase the current tail
  109. grid[new_y][new_x][0] = TAIL; # create a new tail
  110. tail_pos = [new_y, new_x];
  111. }
  112. }
  113. readkey.ReadMode(3);
  114. STDOUT.autoflush(true);
  115. loop {
  116. var key;
  117. while (!defined(key = readkey.ReadLine(-1))) {
  118. move();
  119. display();
  120. Sys.sleep(sleep);
  121. }
  122. given (key) {
  123. when ("\e[A") { if (dir != DOWN ) { dir = UP } }
  124. when ("\e[B") { if (dir != UP ) { dir = DOWN } }
  125. when ("\e[C") { if (dir != LEFT ) { dir = RIGHT } }
  126. when ("\e[D") { if (dir != RIGHT) { dir = LEFT } }
  127. }
  128. }