Python 3.5 Essentials – 1. Output

When you are trying to learn a new language it is always better if you will see a practical real life result. Let’s try to learn Python from zero and develop a chess game.

The first topic to onboard is Output, because it is the only one way for human to get a result of program execution. For our first programs we will use a text output on screen to console (terminal).

For this series of articles I am using Ubuntu 16.04 where Python 2.7.12 and Python 3.5.2 are preinstalled.  I will write an article to make an instructions for using examples on Windows.

To print some text on the screen we should use print() function in Python. You can create chess0.py text file with following code:

print(" abcdefgh ")
print("8RNBQKBNR8")
print("7PPPPPPPP7")
print("6        6")
print("5        5")
print("4        4")
print("3        3")
print("2PPPPPPPP2")
print("1RNBQKBNR1")
print(" abcdefgh ")

To run this code, open console and type following command:

python3 chess0.py

Now, Python 3.5 will interpret your code and print the chessboard on the screen:

 abcdefgh 
8RNBQKBNR8
7PPPPPPPP7
6        6
5        5
4        4
3        3
2PPPPPPPP2
1RNBQKBNR1
 abcdefgh

Usually any console is using monospaced fixed-width fonts, so, all characters should be well aligned.  But i=even if we can understand the figures it does not looks like a chessboard. To make it more realistic we can use escape sequences. Now you can modify your code to add right background colors for chessboard cells:

print("  abcdefgh  ")
print("8 \x1b[38;5;232m\x1b[48;5;231mR\x1b[0m\x1b[38;5;231m\x1b[48;5;232mN\x1b[0m\x1b[38;5;232m\x1b[48;5;231mB\x1b[0m\x1b[38;5;231m\x1b[48;5;232mQ\x1b[0m\x1b[38;5;232m\x1b[48;5;231mK\x1b[0m\x1b[38;5;231m\x1b[48;5;232mB\x1b[0m\x1b[38;5;232m\x1b[48;5;231mN\x1b[0m\x1b[38;5;231m\x1b[48;5;232mR\x1b[0m 8")
print("7 \x1b[38;5;231m\x1b[48;5;232mP\x1b[0m\x1b[38;5;232m\x1b[48;5;231mP\x1b[0m\x1b[38;5;231m\x1b[48;5;232mP\x1b[0m\x1b[38;5;232m\x1b[48;5;231mP\x1b[0m\x1b[38;5;231m\x1b[48;5;232mP\x1b[0m\x1b[38;5;232m\x1b[48;5;231mP\x1b[0m\x1b[38;5;231m\x1b[48;5;232mP\x1b[0m\x1b[38;5;232m\x1b[48;5;231mP\x1b[0m 7")
print("6 \x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m 6")
print("5 \x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m 5")
print("4 \x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m 4")
print("3 \x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m\x1b[38;5;231m\x1b[48;5;232m \x1b[0m\x1b[38;5;232m\x1b[48;5;231m \x1b[0m 3")
print("2 \x1b[38;5;232m\x1b[48;5;231mP\x1b[0m\x1b[38;5;231m\x1b[48;5;232mP\x1b[0m\x1b[38;5;232m\x1b[48;5;231mP\x1b[0m\x1b[38;5;231m\x1b[48;5;232mP\x1b[0m\x1b[38;5;232m\x1b[48;5;231mP\x1b[0m\x1b[38;5;231m\x1b[48;5;232mP\x1b[0m\x1b[38;5;232m\x1b[48;5;231mP\x1b[0m\x1b[38;5;231m\x1b[48;5;232mP\x1b[0m 2")
print("1 \x1b[38;5;231m\x1b[48;5;232mR\x1b[0m\x1b[38;5;232m\x1b[48;5;231mN\x1b[0m\x1b[38;5;231m\x1b[48;5;232mB\x1b[0m\x1b[38;5;232m\x1b[48;5;231mQ\x1b[0m\x1b[38;5;231m\x1b[48;5;232mK\x1b[0m\x1b[38;5;232m\x1b[48;5;231mB\x1b[0m\x1b[38;5;231m\x1b[48;5;232mN\x1b[0m\x1b[38;5;232m\x1b[48;5;231mR\x1b[0m 1")
print("  abcdefgh  ")

Run python3 again:

chess1

Now, it looks nicer but our code became unreadable. How to make it better with help of variables we will cover in the next article.

Congratulations with your first program on Python 3.5!

One block Tetris for Delphi

Here is a Tetris for Delphi example which used buffered GDI graphics.

Tetris Delphi

Controls:

  • LEFT, RIGHT flash arrows – moves
  • DOWN arrow – speed up the block
  • ESC – exit
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
const
  n = 10; // количество строк
  m = 5;  // количество столбцов
  z = 50; // длина стороны квадрата ячейки на поле
  normal_timer = 600; // значение интервала таймера по умолчанию
  speed_timer = 100;   // значение интервала таймера при ускорении
type
  player = record
              x:integer;
              y:integer;
            end;
            // тип запись, для фиксирования положения кубика игрока
var
  cols: array [1..n, 1..m] of boolean; // матрица состояний ячеек поля
                                    // true - кубик есть
                                    // false - кубика нет
  p: player; // летящий вниз кубик
  i,j: integer; // счетчики
  counter: integer;

procedure verify_line;
var
  last_line_full: boolean;
begin
  last_line_full := true; // флаг указывающий на заполнение последней линии

  for i:=1 to m do
  begin
    // проверяем последнюю линию (строку)
    if not cols[n,i] then // если какого-то кубика в линии нет, то говорим, что линия не заполнена и выходим из цикла
    begin
      last_line_full := false;
      break;
    end;
  end;


  // если поледняя линия заполнена, то необходимо сдвинуть все строки на одну ниже
  if last_line_full then
  begin
    counter := counter + 1; // раз заполнили линию , добавляем очки
    for i:=n downto 2 do
    begin
      for j:=1 to m do
      begin
        cols[i,j] := cols[i-1, j]; // заменяем строки которые ниже, строками которые выше
      end;
    end;

    // первую строку очищаем
    for i:=1 to m do
      cols[1, i] := false;
  end;

  Form1.Caption := 'Tetris. count = ' + IntToStr(counter); // выводим результат игрока в заголовок

  // т.к. у нас кубик 1 на 1 клетку, то достаточно проверить заполненность линии 1 раз
  // были бы другие объекты большей высоты, не обходимо было бы проверять, до тех пор
  // пока последняя линия не стала бы незаполненной
end;

procedure new_player;
begin
  // если было ускорение, и даже если его не было
  // возвращаем значение интервала таймера в исходное
  Form1.Timer1.Interval := normal_timer;

  // т.к. процедура new_player не объявлена в классе TForm1,
  // то, чтобы обратиться к объектам на форме
  // нужно перед ними писать Form1.

  // начальное положение игрока
  randomize;
  p.x := Random(m-1)+1; // случайное число от 1 до m
  p.y := 1; // первая строка сверху
end;

procedure  verify_player;
begin
  // проверяем, чтобы кубик не вылетел за границы поля
  if (p.x <= 1) then p.x := 1 else if (p.x >= m) then p.x := m;

  if (cols[p.y, p.x]) then
  begin
    Form1.Timer1.Enabled := false;
    ShowMessage('Игра Окончена!');
  end;

  // если кубик упал до самого низа или если кубик упал на другой кубик
  if (p.y >= n) or (cols[p.y+1, p.x]) then begin
    cols[p.y, p.x] := true;  // отмечаем упавший кубик на поле
    new_player; // возвращаем падающий кубик в начало пути
  end;
end;

//отображает игровое поле
procedure print_m;
var
  b: TBitmap;   // будем использовать двойную буфферизацию
                // поэтому создаем переменную типа TBitmap
                // смысл в том, что сначало рисуем "в памяти",
                // а потом выводим на экран

begin

  verify_line;  // проверим последнюю линию ( строку ) на заполненность
  verify_player; // проверяем координаты игрока
  b := TBitmap.Create; // создаем объект заданного типа


  b.Width := Form1.Width; // устанавливаем ширину рисунка в памяти
  b.Height := Form1.Height; // устанавливаем высоту рисунка в памяти

  // рисуем кубики которые уже упали
  for i:=1 to n do
    for j:=1 to m do
    begin
      if cols[i,j] then
      begin
        b.Canvas.Brush.Color := clRed;  // если есть, то рисуем красным
      end
      else
      begin
        b.Canvas.Brush.Color := clWhite; // если нет, то рисуем белым
      end;

      b.Canvas.FillRect(Rect((j-1)*z,(i-1)*z, (j-1)*z+z, (i-1)*z+z)); // отображаем ячейку
      // обращаем внимание j - это столбцы, оно меняет координату x
      // i - строки, меняет координату y
    end;

  b.Canvas.Brush.Color := clRed; // цвет падающего кубика
  b.Canvas.FillRect(Rect((p.x-1)*z,(p.y-1)*z, (p.x-1)*z+z, (p.y-1)*z+z)); // отображаем ячейку

  // из всех координат на поле вычитаем единичку
  // потому что размер поля например 5 на 10, но в графике это 250 на 500 (потомучто если кубик будет со стороной 1 пиксель его никто не увидит)
  // и в графике координаты начинаються с нуля, т.е. кубик с координатами на поле 1,1 , это прямоугольник (Rect(0,0,50,50))

  // копируем картинку из памяти в картинку на экране,
  // по человечески говоря - отображаем
  Form1.Canvas.CopyRect(Rect(0,0, Form1.Width, Form1.Height), b.Canvas, Rect(0,0,b.Width, b.Height));

  // если не использовать двойной буфер, то все b заменили бы на Form1 (или Image1)
  // удалили бы строки связанные с TBitmap.Create, b.Width, b.Height, Form1.CopyRect

  b.Free; // очищаем память, от временной картинки
end;



procedure TForm1.FormCreate(Sender: TObject);
begin
  // в этой процедуре всегда все сбрасываем на исходные позиции



  counter := 0; // счетчик очков в ноль
  new_player;

  // очищаем поле
  for i:=1 to n do
    for j:=1 to m do
      cols[i,j] := false;

  // устанавливаем размеры окна
  Form1.ClientWidth := m * z;
  Form1.ClientHeight := n * z;
  // используються размеры с приставкой Client потомучто, без неё это размер окна
  // с учетом всех рамок, отступов и в зависимости от установленного стиля
  // графической оболочки виндовс просто размеры Width и Height разные!
  // а ClientWidth и    ClientHeight постоянны всегда и гарантируют нам нужный размер рабочего поля

end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if ( not Timer1.Enabled ) then // если игра окончена
  begin
    exit; // то - не обрабатываем клавиши
  end;

  // если была нажата клавиша влево
  if (Key = VK_LEFT) then
  begin
    p.x := p.x - 1;
    print_m; // если что-то поменялось, сразу отображаем изменения
  end
  // иначе если нажата клавиша вправо
  else if (Key = VK_RIGHT) then
  begin
    p.x := p.x + 1;
    print_m;
  end
  // если нажата клавиша вниз, делаем ускорение
  else if (Key  = VK_DOWN) then
  begin
    Timer1.Interval := speed_timer;
  end
  // если пользователь нажал ESC, то выходим
  else if (Key = VK_ESCAPE) then
    close;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  p.y := p.y + 1; // опускаем кубик вниз
  print_m; // отображаем игровое поле
end;

end.

Sokoban for Turbo Pascal

Here is the simple ASCII text graphics version of Sokoban game for Turbo Pascal.

Controls:

  • w, s, a, d – UP, DOWN, LEFT, RIGHT
  • ESC – exit

Map file example for input:

MAP.TXT
17 10
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 2 0 0 0 0 0 2 0 2 0 4 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1 0 3 0 0 1 0 0 0 1
0 0 0 0 0 0 0 1 0 3 0 0 0 0 0 1 1
0 0 0 0 0 0 0 1 0 3 0 0 0 0 0 1 1
0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1
program sokoban;
uses crt;
type
 sPoint = record
  x:integer;
  y:integer;
 end; { structure type for map point }
 
const
 mxWidth = 100;
 mxHeight = 100; { max width and height of field }
 
 maxBlocks = 100; { max count of blocks }
 
var
 map: array [1..mxWidth, 1..mxHeight] of byte; { map }
 fin: Text; { map file }
 i,j:integer; { counters}
 v:byte; { sokoban player direction }
 m,n: integer; { windth and height of player map } 
 freesq,  { количесво свободных "контейнеров" для ящиков}
 countsq: integer; { количесво ящиков }
 c: char; { код клавиатуры }
 player: sPoint; { координаты игрока}
 nx, ny:integer; { временные переменные для хранения следующей позиции игрока}
 blockpoint: array [1..maxBlocks] of sPoint; { массив ящиков }
 lastpx, lastpy, lastbx, lastby, lastbi, lastc: integer; { временные переменные сохраняющие предпоследнюю позицию игрока }
 f: Boolean;  { флаг совершения хода, чтобы нельзя было отменить ход в самом начале игры }
 
{ возвращает идентификатор (номер) блока расположенного по координатам x,y, если блока нет, возвращает 0} 
function get_block(x,y:integer): integer;
var
 i:integer;
 v: integer;
begin
 v:=0;
 for i:=1 to countsq do { просматриваем массив блоков }
  if (blockpoint[i].x = x) and (blockpoint[i].y = y) then
  begin
   v := i;
   break; { если нашли, то прерываем работу цикла посика }
  end;
 get_block:=v;
end;
 
{пытаеться переместить блок, ели его толкает сокобан, если удалось возвращает значение Истина, иначе Ложь}
function TryMoveBlock(id:integer):Boolean;
var
 nx,ny,x,y:integer;
begin
 x:= blockpoint[id].x;
 y:= blockpoint[id].y; {запоминаем текущие координаты блока}

 lastbx:=x;
 lastby:=y;
 lastbi:=id;
 lastc := freesq; {сохраняем параметры блока, для восстановления если пользовател отменит последний ход}

 case v of {определяем направление движения сокобана, чтобы в этом же направлении сдвинуть блок}
 1: begin nx:=x; ny:=y-1; end; {вверх}
 2: begin nx:=x; ny:=y+1; end; {вниз}
 3: begin nx:=x-1; ny:=y; end; {влево}
 4: begin nx:=x+1; ny:=y; end; {вправо}
 end;

 if (get_block(nx,ny) = 0) and (map[ny,nx] <>1) then  {если дальше нет стены, или второго блока, то двигаем блок}
 begin

      blockpoint[id].x := nx;
      blockpoint[id].y := ny;
      if (map[ny,nx] = 3) and (map[y,x] = 0) then {если блок передвинут с пустой клетки, то уменьшаем счетчик свободных контейнеров}
      begin
         dec( freesq );
      end
      else if (map[ny,nx] = 0) and (map[y,x] = 3) then {иначе если блок передвинут с фыинального места на пустое, возвращаем значения счетчика}
         inc( freesq );

      TryMoveBlock := True;
      exit;
 end;
 TryMoveBlock := False;

end;

{пытаеться переместить сокобана на новые координаты если это возможно}
procedure MoveSokoban(x,y:integer);
var
 bi:integer; {идентификатор блока}
begin
     lastpx:=player.x;
     lastpy:=player.y; { запоминаем позицию сокобана, для последующего восстановления предыдущего хода, если потребуеться}

	 {определяем границы возможного перемещения}
     if ( x>0) and (y>0) and (x<m) and (y<m) then
     if map[y,x] <> 1 then begin {если впереди не стенка}
        bi  := get_block(x,y); { узнаем, есть ли там блок }
        if bi > 0 then {если есть }
        begin
           if (TryMoveBlock(bi)) then {то сначало пробуем переместить блок, а если удалось - перемещаем вслед сокобана}
           begin
                   player.x := x;
                   player.y := y;
           end;
        end
        else {если блока и стены нет, то просто перемещаем сокобана}
         begin
                   player.x := x;
                   player.y := y;
         end;
     end;
end;
begin
 freesq := 1;
 countsq := 0;
 Assign(fin, 'Map.txt');
 Reset(fin); {открываем файл карты}
 Read(fin,m,n); {считываем размеры карты}
 for i :=1 to n do
  for j := 1 to m do
  begin { считываем саму карту }
   Read(fin, v);

   case v of
    1: map[i,j] := 1; {стена}
	  2: map[i,j] := 3; {"контейнер" для ящиков}
    3: begin blockpoint[freesq].x := j; blockpoint[freesq].y := i; inc(freesq);  inc(countsq); end; {блок}
    4: begin player.x := j; player.y := i; end; {игрок}
	else
	 map[i,j] := 0; {пустая клетка}
   end;
  end;
  Close(fin); {закрываем файл карты}
  lastc := freesq;
  f:=False; {запоминаем условия отмены хода}
 while(freesq > 1) do {пока хоть один контейнер свободен}
 begin
   clrscr; {очищаем экран}

   for i:=1 to n do
   begin
    for j:=1 to m do
    begin {отображаем карту}
     gotoxy(j,i);
     TextColor(14);
     if (map[i,j] = 1) then
      write('І')
     else if (map[i,j] = 3) then
      begin
        TextColor(2);
        write('Ь');
      end

     else
      write(' ');
    end;

    writeln;
   end;

   for i:=1 to countsq do
   begin {отображаем блоки}
        gotoxy( blockpoint[i].x , blockpoint[i].y);
        TextColor(5);
        write('Ы');
   end;

   TextColor(14);
   gotoxy(player.x, player.y);
   write(''); {отображаем игрока}

   gotoxy(80,25); {убираем курсор, чтобы не мозолил глаз}
   c := ReadKey;
   case c of {проверяем ввод с клавиатуры}
   'w': begin nx:=player.x; ny:=player.y-1; v:=1; end; {ход вверх}
   's': begin nx:=player.x; ny:=player.y+1; v:=2; end; {ход вниз}
   'a': begin nx:=player.x-1; ny:=player.y; v:=3; end; {ход влево}
   'd': begin nx:=player.x+1; ny:=player.y; v:=4; end; {ход вправо}

   'z': if f then begin
             freesq := lastc;
             player.x := lastpx;
             player.y := lastpy;

             blockpoint[lastbi].x := lastbx;
             blockpoint[lastbi].y := lastby;
             continue;
   end;
   #27: break; {выход }
   end;
   f:=True;
   MoveSokoban(nx,ny); {пытаемся переместить сокобан}
 end;
 clrscr;
 textcolor(15);

 if (freesq = 1) then {сообщаем пользователю о выйгрыше если ему удалось заполнить все контейнеры}
  writeln('You Win!')
 else {или о проигрыше если , он самостоятельно прервал игру}
  writeln('You Lose!');
 writeln('Press Enter for exit!');
 readln;
end.

Snake for Turbo Pascal

Snake is another ASCII graphics game example for Turbo Pascal additionally to Sokoban.

program  csnake;
uses crt;
type
 sPoint = record
  x:integer;
  y:integer;
 end; {тип для точки} 
const
 maxWidth = 40; {максимальна длинна змейки} 
var
 width: integer; {длина змейки}
 e :sPoint; {координаты еды} 
 snakebody: array [1..maxWidth] of sPoint; {массив с координатами частей тела змейки} 
 i, {считчик цикла} 
	nx, ny:integer {координаты головы змейки} ; 
 v: byte; {вектор направления головы змейки} 
 gameover: boolean; {флаг окончания игры} 
 key: char; {буфер клавиши} 

 {проверяет свободна ли клетка и не выходит ли она за границы поля} 
function CheckNext(x:integer; y:integer):boolean;
var
 isset: Boolean; {флаг проверки занятости клетки или выхода за границы поля} 
 i: integer;
begin
  isset := True;
  for i:=1 to width do
  begin {проверяем нет ли на места клетки змейки} 
   if (x = snakebody[i].x) and ( y = snakebody[i].y ) then
      isset := False;
  end;
  if isset then
  begin {проверяем не вышла ли голова змейки за границы поля} 
   if (x <= 0) or (y<=0) or (x>=80) or (y>=25) then isset := False;
  end;
  CheckNext := isset;
end;

{создает еду на поле} 
procedure NewEat;
begin
 e.x := random(78) + 2;
 e.y := random(23) + 2; {генерируем координаты еды} 

 {если место занято, генерируем еще раз, и так пока не найдем свободное} 
 while (not CheckNext(e.x, e.y)) do
 begin
  e.x := random(78) + 2;
  e.y := random(23) + 2;
 end;
end;

{перемещает змейку}
procedure MoveSnake(x,y:integer);
var
 i:integer; {счетчик цикла} 
begin
 for i:=1 to width do
 begin
  snakebody[i] := snakebody[i+1]; 
 end; {перещаем все состовляющие змеки, на место соседнего} 
 
 snakebody[width].x := x;
 snakebody[width].y := y; {голове змейки присваиваем новые координаты} 
 
 if (x = e.x) and (y = e.y) then {если голова нашла еду} 
 begin
  width := width + 1; {добавляем еще 1 клетку к змейке} 
  snakebody[width].x := x;
  snakebody[width].y := y;
  NewEat; {создаем новую еду} 
 end;
end;

begin
 gameover := false;
 {выстраиваем начальное тело змейки} 
 width := 4;

 snakebody[1].x := 1;
 snakebody[1].y := 1;

 snakebody[2].x := 2;
 snakebody[2].y := 1;

 snakebody[3].x := 3;
 snakebody[3].y := 1;

 snakebody[4].x := 4;
 snakebody[4].y := 1;

 clrscr;
 v  := 1;

 Randomize;

 NewEat; {создаем еду} 

 while(width < maxWidth) do {пока длина змейки меньше заданной} 
 begin

   if (KeyPressed) then {если была нажата клавиша} 
   begin
     key:=Readkey; {считываем её} 
     case key of
      'w': begin if v = 4 then continue; v := 3;  end; {вверх}  
      's': begin if v = 3 then continue; v := 4;  end; {вниз} 	  
      'a': begin if v = 1 then continue; v := 2;  end; {влево} 
      'd': begin if v = 2 then continue; v := 1;  end; {впправо} 
      #27: break; {выход} 
     end;
   end;

   clrscr;

   {поеределение направления движения головы змейки}
   case v of
    1: begin nx := snakebody[width].x + 1;  ny := snakebody[width].y; end; {вправо}
    2: begin nx := snakebody[width].x - 1;  ny := snakebody[width].y; end; {влево}
    3: begin nx := snakebody[width].x;  ny := snakebody[width].y - 1; end; {вверх}
    4: begin nx := snakebody[width].x;  ny := snakebody[width].y + 1; end; {вниз}
   end;

   {если впереди граница или препятствие} 
   if (not CheckNext(nx,ny)) then
   begin
    gameover := true; {заканчиваем игру с поражением пользователя}
    break;
   end;

   MoveSnake(nx, ny); {перемещаем змейку} 

   gotoxy(e.x, e.y);
   write(''); {отображаем еду} 


   for i:=1 to width-1 do
    begin {отображаем тело змейки} 
     gotoxy(snakebody[i].x, snakebody[i].y);
     write('Ы');
    end; 
   gotoxy(snakebody[width].x, snakebody[width].y);
   write('+'); {отображение головы змейки} 

   gotoxy(80,25); {уводим курсор, чтобы не мешал} 

   Delay(30000); {задержка, для разных процессоров разная!}  
 end;

 clrscr;

 if (gameover) then {выводим сообщение пользователю о выйгрыше или проигрыше} 
  WriteLn('GAME OVER!')
 else
  WriteLn('YOU WIN!');

  WriteLn('Press Enter for exit!');
  readln;
end.