snake_game.pl 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #!/usr/bin/perl
  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. use utf8;
  8. use 5.010;
  9. use strict;
  10. use warnings;
  11. use Time::HiRes qw(sleep);
  12. use Term::ANSIColor qw(colored);
  13. use Term::ReadKey qw(ReadMode ReadLine);
  14. binmode(STDOUT, ':utf8');
  15. use constant {
  16. VOID => 0,
  17. HEAD => 1,
  18. BODY => 2,
  19. TAIL => 3,
  20. FOOD => 4,
  21. };
  22. use constant {
  23. LEFT => [+0, -1],
  24. RIGHT => [+0, +1],
  25. UP => [-1, +0],
  26. DOWN => [+1, +0],
  27. };
  28. use constant {BG_COLOR => 'on_black'};
  29. use constant {
  30. SNAKE_COLOR => ('bold green' . ' ' . BG_COLOR),
  31. FOOD_COLOR => ('red' . ' ' . BG_COLOR),
  32. };
  33. use constant {
  34. U_HEAD => colored('▲', SNAKE_COLOR),
  35. D_HEAD => colored('▼', SNAKE_COLOR),
  36. L_HEAD => colored('◀', SNAKE_COLOR),
  37. R_HEAD => colored('▶', SNAKE_COLOR),
  38. U_BODY => colored('╹', SNAKE_COLOR),
  39. D_BODY => colored('╻', SNAKE_COLOR),
  40. L_BODY => colored('╴', SNAKE_COLOR),
  41. R_BODY => colored('╶', SNAKE_COLOR),
  42. U_TAIL => colored('╽', SNAKE_COLOR),
  43. D_TAIL => colored('╿', SNAKE_COLOR),
  44. L_TAIL => colored('╼', SNAKE_COLOR),
  45. R_TAIL => colored('╾', SNAKE_COLOR),
  46. A_VOID => colored(' ', BG_COLOR),
  47. A_FOOD => colored('❇', FOOD_COLOR),
  48. };
  49. my $sleep = 0.05; # sleep duration between displays
  50. my $food_num = 1; # number of initial food sources
  51. local $| = 1;
  52. my $w = eval { `tput cols` } || 80;
  53. my $h = eval { `tput lines` } || 24;
  54. my $r = "\033[H";
  55. my @grid = map {
  56. [map { [VOID] } 1 .. $w]
  57. } 1 .. $h;
  58. my $dir = LEFT;
  59. my @head_pos = ($h / 2, $w / 2);
  60. my @tail_pos = ($head_pos[0], $head_pos[1] + 1);
  61. $grid[$head_pos[0]][$head_pos[1]] = [HEAD, $dir]; # head
  62. $grid[$tail_pos[0]][$tail_pos[1]] = [TAIL, $dir]; # tail
  63. sub create_food {
  64. my ($food_x, $food_y);
  65. do {
  66. $food_x = rand($w);
  67. $food_y = rand($h);
  68. } while ($grid[$food_y][$food_x][0] != VOID);
  69. $grid[$food_y][$food_x][0] = FOOD;
  70. }
  71. create_food() for (1 .. $food_num);
  72. sub display {
  73. print $r, join(
  74. "\n",
  75. map {
  76. join(
  77. "",
  78. map {
  79. my $t = $_->[0];
  80. my $p = $_->[1] // '';
  81. my $i =
  82. $p eq UP ? 0
  83. : $p eq DOWN ? 1
  84. : $p eq LEFT ? 2
  85. : 3;
  86. $t == HEAD ? (U_HEAD, D_HEAD, L_HEAD, R_HEAD)[$i]
  87. : $t == BODY ? (U_BODY, D_BODY, L_BODY, R_BODY)[$i]
  88. : $t == TAIL ? (U_TAIL, D_TAIL, L_TAIL, R_TAIL)[$i]
  89. : $t == FOOD ? (A_FOOD)
  90. : (A_VOID);
  91. } @{$_}
  92. )
  93. } @grid
  94. );
  95. }
  96. sub move {
  97. my $grew = 0;
  98. # Move the head
  99. {
  100. my ($y, $x) = @head_pos;
  101. my $new_y = ($y + $dir->[0]) % $h;
  102. my $new_x = ($x + $dir->[1]) % $w;
  103. my $cell = $grid[$new_y][$new_x];
  104. my $t = $cell->[0];
  105. if ($t == BODY or $t == TAIL) {
  106. die "Game over!\n";
  107. }
  108. elsif ($t == FOOD) {
  109. create_food();
  110. $grew = 1;
  111. }
  112. # Create a new head
  113. $grid[$new_y][$new_x] = [HEAD, $dir];
  114. # Replace the current head with body
  115. $grid[$y][$x] = [BODY, $dir];
  116. # Save the position of the head
  117. @head_pos = ($new_y, $new_x);
  118. }
  119. # Move the tail
  120. if (not $grew) {
  121. my ($y, $x) = @tail_pos;
  122. my $pos = $grid[$y][$x][1];
  123. my $new_y = ($y + $pos->[0]) % $h;
  124. my $new_x = ($x + $pos->[1]) % $w;
  125. $grid[$y][$x][0] = VOID; # erase the current tail
  126. $grid[$new_y][$new_x][0] = TAIL; # create a new tail
  127. # Save the position of the tail
  128. @tail_pos = ($new_y, $new_x);
  129. }
  130. }
  131. ReadMode(3);
  132. while (1) {
  133. my $key;
  134. until (defined($key = ReadLine(-1))) {
  135. move();
  136. display();
  137. sleep($sleep);
  138. }
  139. if ($key eq "\e[A" and $dir ne DOWN ) { $dir = UP }
  140. elsif ($key eq "\e[B" and $dir ne UP ) { $dir = DOWN }
  141. elsif ($key eq "\e[C" and $dir ne LEFT ) { $dir = RIGHT }
  142. elsif ($key eq "\e[D" and $dir ne RIGHT) { $dir = LEFT }
  143. }