2

This is an school assignment. To create a snake game. So I have created two packages one for graphics and one for the snake. The snake moves smoothly and everything works. But I need to control the snake with the keyboard.This is the main procedure:

with Graphics; use Graphics;
with Graphics.Snake; use Graphics.Snake;

procedure Run_Snake is
   B : Buffer (1 .. 24, 1 .. 80);
   S : Snake_Type (1 .. 5) := ((10, 10),
                               (10, 11),
                               (10, 12),
                               (11, 12),
                               (12, 12));
   D : Duration := 0.07;
begin

  loop
      Empty (B);
      Draw_Rect (B, (1, 1), Width  => 80,
                 Height            => 24);
      Draw (B, S);
      Update (B);



      Move (S, 0, -1);
      delay D;

   end loop;

end Run_Snake;

in this line of code I controll the snakes head rotation:

Move (S, x, y);  

where x is the x value it can be -1 for left, 1 for right.
where y is the y value it can be -1 for down, 1 for up;

Anyways, how can I read the input without pausing the snake from moving? Thanks

  • I have already looked at the tetris game but that game is using GTKADA. –  Nov 28 '12 at 06:56
  • 1
    ...and what are you using? Win32? X Window? Something else? – egilhh Nov 28 '12 at 07:39
  • 3
    Try `Get_Immediate`: http://www.ada-auth.org/standards/12aarm/html/AA-A-10-7.html#p11 – Simon Wright Nov 28 '12 at 08:59
  • @SimonWright - It used to be that `Get_Immediate` with most compilers still wouldn't return anything until the user had hit the enter/return key. Has that changed? – T.E.D. Nov 28 '12 at 14:32
  • If you're using GtkAda, there's a `Gdk_Event_Key` example cited [here](http://stackoverflow.com/a/13535772/230513). – trashgod Nov 28 '12 at 17:23
  • @T.E.D., With GNAT on MAC OS X, you don't need to hit return but `Get_Immediate` returns `Available` as `True` twice per keypress! Nothing is perfect :-) – Simon Wright Nov 28 '12 at 18:22
  • Simin Wright was right. Get_Immediate works on Terminal in Mac OS X. However sometimes it will scroll the Terminal Window and the graphics of the game become very unstable. I will try to fix it now :) Thank you –  Nov 29 '12 at 00:24

2 Answers2

4

You might want to use a multitasking system to overcome your problem.

procedure Snake_Game is

   task Run_Snake is
      entry Input_Received (S : Snake_Type; x : Integer; y : Integer);
   end Run_Snake;

   task body Run_Snake is
      D : constant Duration := 0.07;
      B : Buffer (1 .. 24, 1 .. 80);
   begin
      loop
         select
            accept Input_Received (S : Snake_Type; x : Integer; y : Integer) do
               Move (S, x, y);
            end;
         or
            delay D;
            Empty (B);
            Draw_Rect (B, (1, 1), Width => 80, Height => 24);
            Draw (B, S);
            Update (B);
         end select;
      end loop;
   end Run_Snake;

   S : Snake_Type (1 .. 5) := ((10, 10),
                              (10, 11),
                              (10, 12),
                              (11, 12),
                              (12, 12));

begin
   loop
      Do_Whatever_You_Want_To_Get (x, y)
      Run_Snake.Input_Received(S, x, y);
   end loop;
end Snake_Game;

In such a system, you have your drawing process in a separate task. At the "select" line, the task waits for a call to Input_Received(). If this entry is not called after a duration D, then the task executes all the drawing functions, and the loop begins anew.

Hope it can help. Cheers.

tvuillemin
  • 1,148
  • 3
  • 10
  • 25
  • ...also, he'd probably want to verify that whatever code being used from his `Graphics` packages in more than one task is task-safe. – T.E.D. Nov 28 '12 at 14:40
  • Thank you. I am very interested in this multi tasking solution. But I cant get it to work: http://pastebin.com/XxJPzWv3 Am I doing it in the right way? What does "actual for "x" must be a variable" mean? What is an actual? –  Nov 29 '12 at 00:32
  • @Alex90 Hum... I guess the first argument of your Move() function is something like "ASnake : in out Snake_Type", isn't it ? Could you please try this http://pastebin.com/8QUqaQTs ? – tvuillemin Nov 29 '12 at 09:58
2

There are a couple of basic approaches to solving this sort of problem.

The first is to use some kind of non-blocking call that checks for input, but returns immediately whether it finds any input or not. As Simon Wright mentioned in the comments, Ada provides such a call: Get_Immediate. The little nit with this routine is that (when last I checked) most compilers implement it in a way that still requires the user to hit the enter key before their input is available to that routine. Most OS's will have a system call for such an activity too (without that annoying enter key drawback), but blocking is usually their preferred behavior, so setting up non-blocking input is often difficult or arcane.

I don't know what's in that Graphics package of yours, but you may want to look through it to see if it possesses such a call. I'm guessing this is a school programming assignment, and that is a package built to use with this assignment. If so, there's probably some facility in there for this, or else your instructor did not picture your game working that way.

The other approach is to use a different thread of control for reading user input. That way you can use the blocking calls that OS's love in one thread, and let the rest of your game merrily chug away as it should in the other. Ada implements this with tasks (as shown in tvuillemin's answer). The issue here is that any interactions between those two tasks (eg: passing the input to the game task) has to be properly synchronized. Also, any package or facility used by both tasks must be task-safe. For Ada packages you are pretty safe, as long as you don't try to share file objects or something. But for third party packages (like Graphics?) you are generally best just picking one task to "own" that package.

Most windowing systems solve this problem in an incredibly complex way. They implement their own "main loop" that is supposed to take over your thread of control, and it will take care of mundane duties like refreshing the contents of windows and polling for input. If you want to do something custom (eg: update game state periodically), you have to put it in a routine and register it as a "callback".

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
  • Thank you very much for your great explanation. Yes this is a school assignment. We got only the loop code that I have posted. And we would figure out the rest and create the packages. Get_Immediate worked but it did mess up the graphics. –  Nov 29 '12 at 00:36