Shooting game in scheme


1.Several premises

When one attempt to make program, it is nessary to list up several premises.

That's all.
I learned program design from several website (thank you!)

This game does not work on usual terminal (non X11 window system). Because I use xset command in order to control key-auto-repeat.



2.Program

Here is the program. It comes with GPL version3.
Please read LICENSE and README. It is easy to run program.

This is newer version. For guile-2.2.x and Linux. May be, this is better.




3.About curses library

It is absolutely nessary to use curses library. First of all, SCM was my principal scheme implementation. SCM has curses library in it. But I could not use this library. Other implementations have also curses libraries. But I could not use them too. I was at a loss.....OH...
Perhaps I need a lot of time to read documents to become to be able to use curses library. May be,, it is better to make it by myself, rather than spend lot of time to read documents. (^^;;;
So I decieded to make it only for character terminal (not for graphical terminal)

There are many websites which describe about VT100 escape sequence. I watched these sites.
If I could not find function that I need, then I picked up them from another way. For example, I used stty command.
It is for (cursor-visible , cursor-invisible) or (echo , noecho) or (cbreak , nocbreak).
I also used xset command to control auto-key-repeat. So this game can work only in X11 system. (X terminal emulator)




3.Jump to next step without reading

Next program stops when no input data and not EOF.

  int main(int ac, char **av){
    int   c;
    while((c = getchar()) != EOF)
      putchar(c);
    return 0;
  }

Above program stops when nobdy input charcter.
But in case of GAME, program should go to next statement or step without reading.
So, I should find the method that teaches me how to go to next step without reading.
I looked at many website. Then I found that. Next command can do it!

  $ stty -icanon min X  time Y

Let's try. Next program do this.

#!/usr/local/bin/scm
;
; min must be 0. but time 0, 2, 5, 10 every-case ok!
;
(define $ip (current-input-port))
(define (rewind p) (file-position p 0))
(define (setting)    (system "stty -icanon min 0 time 2"))
(define (restore-io) (system "stty icanon"))
(define (getch p) (rewind p) (read-char p))
;
(define (get-word p)
  (rewind p)
  (let ((tmp (read-line p)))
    (display "********* ")
    (display tmp)
    (display " **********")
    (newline)))
;
(define (test)
  (setting)
  (do
      ((key (getch $ip) (getch $ip)))
      ((and (char? key) (char=? key #\q))
       (restore-io)
       (get-word $ip)
       (exit))
    (display "++++++++    ")))
;
(test)
;

I used (rewind) in order to flush input buffer. But this may be verbose.
When you run this script, ++++++ signs appear permanently. This means program goes to next statement without reading.
When you input "q", program stops and wait your next input. At this time, (restore-io) is done, so that "-icanon hoge hoge" is aborted.
When you change the number of "time", example 0, 2, 5, 10. Speed changes.

The reason why I explain this is that some trouble happened in some scheme implementasion.
SCM is ok. Every case of every time Y, SCM works well.
Chicken is also ok in every case.
Gauche works well just only when min is 0 and time is 0. Another case , gauche does not work.
Guile 1.8 can't treat icanon. It does not work in every case.
Later I noticed that guile 2.0 works well in every case of min and time. But at this time, I used guile 1.8,,, OH OH.

I decided to use another method instead of icanon. I read documents. I know the function "char-redy?". I thought that this must be usefull to go next statement without reading. And I tried.

(define (getch2)
  (if (char-ready? $ip) (read-char $ip) #f))

This function works well instead of icanon. So I used this (above) method in my game program.

Later I thought that this method (char-redy? and read-char) is better than to use icanon. Because this function is in scheme!




4.Measure millisecond in program

Next, I tried to measure millisecond in game program in order to control game speed. Another method should not work well.
If I use loop count, in case of fast CPU, game goes very fast. In case of slow (not fast) CPU, game goes slowly. This is not good.(^^;;;
Once more I read documents and I know that SCM can't measure millisecond ! I was at a loss....
I decided to change my main implementation from SCM to guile (1.8 and 2.0).
Guile chicken and gauche can do this!
The function in each implementation is below.

gosh           (sys-gettimeofday) ;return value is multiple value!!
guile          (gettimeofday)
racket         (current-inexact-milliseconds)
chicken        (current-milliseconds)

If I use these functions above, program code will become implementation dependent. So I used my function that can negate implementation dependency. The name of my function is stop-watch (hahaha...). This function is like object-oriented.
It works by giving message. Example is below.

  (define time1 (stop-watch))
  (define time2 (stop-watch))
  (time1 'set)
  -----
  -----
  (time2 'set)
  -----
  -----
  (time2 'get)
  -----
  (time1 'get)
  -----
  -----

Time1 and time2 work independent. So that I can use 2 stop-watches in same program.
Set orders to reset time 0. Get orders to get time from reset.
Please see game program.




5.About game

[J] key want ME (A) to go left. [L] key is right. You can shoot beam by pressing space-bar.
Enemy sometimes warps to some place. I use random function to decide how many steps enemy walks. I also use random to decide either ho goes to right or left. And I use random to decide where he suddunly appears.(^^)
The speed of walking, number of beams can be controled by fix numbers in the program. But perhaps these numbers I decided should be most interesting. (^^)
Now please read README and enjoy !