2 years ago 2 years ago C Share

Eel – Play This DOS Game in C in Your Browser

On this page you can see the source code and other details of the game Eel, which I wrote in C on a Windows PC while I was studying Physics in 1997.

I'll add more info as I get around to it.

You can play the game live on this page, and view the C source code.

The PC emulation is done using js-dos.

The "DOS Zone Studio" here allowed me to create a .jsdos module file from my original eel.exe DOS program, you can use it to make a runnable DOS disk from any actual PC files or games you may have from that era.

You can read here my description of how a DOS program can be compiled on a modern computer in 2021, and how I made a recent edit to the game to use the I, J, K, and L, keys to steer/turn the Eel, instead of the keyboard's arrow keys.


Below you can see the source code of Eel in C. It was compiled to an .exe using a GNU C compiler. It used to run in the Command Prompt in Windows up to about Windows XP, maybe Windows Vista(?), it doesn't work in Windows 10 and I don't think (from memory) it worked in Windows 7 either.

Now, thanks to JavaScript and Web Assembly, you can run it in any modern/recent web browser.

HOW TO PLAY EEL

Use the I, J, K, and L keys to turn.

If you crash into anything except the "food" items, you die.

It's basically like the "snake" games which were featured for e.g. on the early Nokia phones. I made it this version into an undersea theme for some variety.

In the original version it used the arrow keys, but these keyboard keys seem to still be active in their usual function (of scrolling the browser screem), even when using the game window itself. Which was a bit dizzy (the screen scrolls up and down like you were normally scrolling a browser window with the arrow keys), in the middle of the game. You can stop this by using the full-screen mode.

So I made a modified version for this emulator, which uses the I, J, K, and L keys to turn.

This also means that it should now be playable on phones and tablets using the virtual popup keyboard. Click on pen-with-tablet icon — 2nd from the bottom of the left hand side of the emulator window).

You may have to turn the sound on and off to get the sound to work, maybe, depending on how strict your web browser is about auto-playing sound on web pages. The sound is pretty basic (in terms of being low-quality, it does sound authentically vintage though), you can see how it works from the C code if you know a little about C.

C Source Code of Eel

#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <pc.h>
#include <stdlib.h>

#define UP           105    /* = I, and 328 was up    arrow key */
#define DOWN         107    /* = K, and 336 was down  arrow key */
#define LEFT         106    /* = J, and 331 was left  arrow key */
#define RIGHT        108    /* = L, and 333 was right arrow key */

#define FISH         224
#define HOOK         168
#define FILLCHAR     250

#define CRAB         42
#define BOXFISH      254
#define GUPPIES      247

#define HEADCOL      2
#define BODYCOL      8

           /*  0    1    2    3  */
enum movetype {up,down,left,right};

int snakex [500] = {20,20,20,20,20} , snakey [500] = {19,20,21,22,23};
int fish[  ] = {224,224,224,42,224,224,42,42,157,224,
                224,174,224,239,42,157,42,174,42,224,42,
                157,174,239,247}, maxfish = 24;
int fishleft[25] = {5, 7, 5, 6, 10, 5, 4, 7, 3, 5,
                                     5,3,7,1,12,10,5,7,7,5,
                                     10,7,4,1,1};

int screencolours[5]={LIGHTBLUE, LIGHTCYAN, LIGHTGREEN, LIGHTMAGENTA,
                      LIGHTRED};
int titledata[26] = {3,3,3,3,3,0,0,2,2,2,2,2,1,1,1,1,3,3,3,3,3,
                     2,0,0,0,0};

char sparestr[41];
int snakel = 5, score, hiscore, snakehead = 219, snakebody = 219;
int fishx,fishy, dead, currfish, fishgot, lives = 3, hit, screen;
int fishleftnow, counter, speed, soundmessage, bordercol;

int fishsong[9] = {0,0,1047, 1175,  1480, 1568,  1480, 1568, 1760};
int fishsongl = 8, nodrawx1, nodrawx2, nodrawy1, nodrawy2, nextextra;

int octsong[ ] = {2349, 4, 2349, 4, 2093, 4, 2349, 12,   0, 12, 
                  1975, 4, 1975, 4, 1760, 4, 1975, 12,   0, 12,
                  1975, 2, 1760, 2, 1568, 4, 1568, 4, 1568, 2,
                  2349, 2, 0, 4, 2093, 4, 1975, 4, 1760, 2,
                  1975, 4, 0, 2, 2093, 8, 0, 0,0,0};

int diesound[ ] = {1047, 1480, 2093, 1175, 1661, 2349, 2217, 2093, 0};

enum movetype snakedir = up;

int getrandom (int);
void drawfish (int);
void drawhook (void);
void drawlives (int);
void drawscore (void);
void cleargamearea (int);
void drawsnake(void);
void drawscreenboxes (void);
void drawtitle (void);
void soundplay (void);

void clearkeybuff (void)
{
    while (kbhit())  getkey();
}

void initgame (void)
{

    int a;

    clearkeybuff();
    screen = 0; bordercol = screencolours[(screen / 5) % 5];
    lives = 3; score = 0; speed = 3; nextextra = 500000;
    
    fishleftnow = fishleft[screen % 25] * (screen / 25 + 1);    
    currfish = screen % 25;
}

void resetsnakepos (void)
{
    register int a;

    clearkeybuff();
    counter = 0;
    snakex[0]=6;snakex[1]=5;snakex[2]=4;snakex[3]=3;snakex[4]=2;
    snakey[0]=23;snakey[1]=23;snakey[2]=23;snakey[3]=23;snakey[4]=23;
    snakel = ((screen % 25) + 1) * 5; snakedir = right;
    for (a=5;a 2)
    {
        snakex[5]=20;snakex[6]=20;snakex[7]=20;snakex[8]=20;snakex[9]=20;
        snakey[5]=23;snakey[6]=22;snakey[7]=21;snakey[8]=20;snakey[9]=19;

        for (a=10;a<=delay;a++)
    {
        t = gett();
        while (gett() == t) ;
    }
}

void playoctsong(void) 
{
     int a,b,d;
     a=0;

     do
     {
        sound(octsong[a]/4);
        wait(octsong[a+1]);
        a += 2;
     }
     while (octsong[a] || octsong[a+1]);
    sound(0);
}

void clrtopscr(void)
{
    register int a;
    for (a=1;a<25;a++)
    {
        gotoxy(1,a);
        clreol();
    }
}

int readscreen (int x, int y)
{
    unsigned char *ascii;
    unsigned char value;
    ascii = &value;
    gettext(x,y,x,y,ascii);
    return  (int) value;
}

int isborder (int c)
{
    
    if ( (c > 178 && c < 219) || (c == FILLCHAR) )  return 1;
        else return 0;
}

void newscreen(void)
{
    sound (0);   screen++;
    bordercol = screencolours[(screen / 5) % 5];
    drawsnake();
    
    if (screen > 24)
    {
        speed = 2;        
    }
    cleargamearea(1);    
    
    drawscreenboxes();
    resetsnakepos();
    drawsnake();
    fishleftnow = fishleft[screen % 25] * (screen / 25 + 1);
    
    currfish = screen % 25;
        
    drawscore();
    wait(30);
    clearkeybuff();
}

void hitfish (void)
{

    switch (fish[currfish])
    {
        case 224:
        score += 1000;
        break;

        case 42:
        score += 3000;
        break;

        case 157:
        score += 5000;
        break;

        case 174:
        score += 10000;
        break;

        case 239:
        score += 50000;
        break;

        case 247:
        score += 100000;
        break;
    }

    fishgot++; fishleftnow--;
    drawsnake();
    drawscore();
    soundmessage = 8;

    if (!fishleftnow)
    {
        do
        {
            soundplay();
            wait(speed);
        }
        while (soundmessage > 2);
        sound(784/2); wait(speed);
        sound(880/2); wait(speed);
        sound(988/2); wait(speed);
        sound(1047/2); wait(speed);
        sound(0); soundmessage = 0;
        newscreen();
    }

    if (getrandom (10) < 2) drawhook();

    drawfish(fish[currfish]);
    
    snakex [snakel] = snakex [snakel - 1]; 
    snakey [snakel] = snakey [snakel - 1]; 
    snakel++;
}

void lostlife (void)
{
    int a,b,c;
    c = 0; b = 0;
    if (counter < snakel - 5) c = snakel - 5 - counter;
    sound (0); soundmessage = 0;
    for (a=snakel - 1 - c;a>0;a--)
    
    {
        if (a % 2  && b < 10)
        {
            sound(diesound[b]/4);
            b++;
        }
        wait(1);
        gotoxy(snakex[a],snakey[a]);
        cprintf (" ");
    }

    for (;b<10;b++)
    {
        sound(diesound[b]/4);
        wait(2);
    }
 
    lives -= 1;
    dead = 1;
}

void hitsomething (int hitchar)
{
    int a;

    if ( (isborder (hitchar)) || hitchar == HOOK || hitchar == 219)
        lostlife();

    
    if (hitchar == fish[currfish])
        hitfish();            
}

void drawsnake(void) /* used for initial stationary snake at screen start */
{
    register int a;

    gotoxy(snakex[0],snakey[0]);
    textcolor(HEADCOL);
    cprintf("%c",snakehead);
        
    for (a=1;a= snakel - 5)
    {
        
        lastx = snakex [snakel - 1];
        lasty = snakey [snakel - 1];
        gotoxy (lastx,lasty);   cprintf(" ");
    }
    textcolor(BODYCOL);
    gotoxy (snakex[0],snakey[0]); cprintf("%c",snakebody);


    for (count = snakel - 1 ; count>0 ; count--)
    {
        snakex[count] = snakex[count - 1];
        snakey[count] = snakey[count - 1];
    }
    
    if (movedir == up)
    {
        snakey[0] = snakey[1] - 1;
        nodrawx1 = nodrawx2 = snakex[0];
        nodrawy1 = snakey[0] - 1; nodrawy2 = snakey[0] - 2; 
    }
    
    if (movedir == down)
    {
        snakey[0] = snakey[1] + 1;
        nodrawx1 = nodrawx2 = snakex[0];
        nodrawy1 = snakey[0] + 1; nodrawy2 = snakey[0] + 2;
    }

    if (movedir == left)
    {
        snakex[0] = snakex[1] - 1;
        nodrawy1 = nodrawy2 = snakey[0];
        nodrawx1 = snakex[0] - 1; nodrawx2 = snakex[0] - 2;
     }

    if (movedir == right)
    {
        snakex[0] = snakex[1] + 1;
        nodrawy1 = nodrawy2 = snakey[0];
        nodrawx1 = snakex[0] + 1; nodrawx2 = snakex[0] + 2;
    }
    
    if (!titledraw) 
    {
        ranover = readscreen(snakex[0],snakey[0]);
    
        if (ranover != 32) hitsomething(ranover); 

        if (dead) return;
    }

    textcolor(HEADCOL);
    gotoxy (snakex[0], snakey[0]);  cprintf("%c",snakehead);
}

void keyaction (void)
{
    int key;
    key = getkey();
    
  if (key == UP && (snakedir == left || snakedir == right)) snakedir = up;
  if (key == DOWN && (snakedir == left || snakedir == right)) snakedir = down;
  if (key == LEFT && (snakedir == up || snakedir == down)) snakedir = left;
  if (key == RIGHT && (snakedir == up || snakedir == down)) snakedir = right;
}

void drawborder(int left, int top, int right, int bottom, int colour
         , int filled)
{   
     register int a,b;
     textcolor(colour);

     gotoxy(left,top);   cprintf("%c",218);
     gotoxy(right,top);  cprintf("%c",191);
     gotoxy(left,bottom);  cprintf("%c",192);
     gotoxy(right,bottom);  cprintf("%c",217);

     for(a=left + 1;a nextextra)
    {
        if (lives < 3) lives++;
        drawlives(lives);
        nextextra += 500000;
    }
    
    gotoxy(2,25);
    textcolor(7);
    cprintf("%08d",score);
    gotoxy(22,25);
    cprintf("%02d",screen + 1);
    gotoxy(27,25);
    cprintf("%02d",fishleftnow);
}

void drawhiscore (void)
{
    textcolor (7);
    gotoxy(32,25);
    cprintf("%08d",hiscore);
}    

void drawlives (int lives)
{
    register int a;
    for (a=1;a<21 ; a++)
        cprintf (" ");

}

void cleargamearea(int flag)
{
    register int a,b;

  if (flag == 0)
  {
    for (a=2;a<40;a++)
        for (b=2;b<24;b++)
            {
            gotoxy(a,b);
            if (readscreen(a,b) == 32) continue;
            if (!isborder(readscreen(a,b)))
                cprintf(" ");        /* flag = 0 means don't clear boxes */
            }
  }
  else
  {
    for (a=2;a<24;a++)
        {
        gotoxy(2,a);
            cprintf("                                      ");  
        }
  }

}

int getrandom (int range)
{
    int value;
 /* value = rand() * range / 65535; This caused a segmentation fault with the 
 									newer C compiler! */
    value = rand() % range;
    return value;
}

void drawfish (int thing)
{
    int a,fishx,fishy;
    do
    {
        fishx = getrandom(35) + 2;
        fishy = getrandom(20) + 2;
    }
    while (readscreen (fishx, fishy) != 32);

    gotoxy(fishx, fishy);
    do
    {
        a = getrandom(6) + 9;
    }
    while (a == screencolours[(screen / 5) % 5]);
    textcolor(a);
    cprintf("%c",thing);
}

void drawhook (void)
{
    int fishx,fishy;
    do
    {
        fishx = getrandom(35) + 2;
        fishy = getrandom(20) + 2;
    }
    while (readscreen (fishx, fishy) != 32 || (fishx == nodrawx1 &&
        fishy == nodrawy1) || (fishx == nodrawx2 && fishy == nodrawy2));

    
    gotoxy(fishx, fishy);
    textcolor(7);
    cprintf("%c",HOOK);
}

void titlescreen (void)
{
    int a,c,s;
    struct time ti;
    clearkeybuff();
    clrtopscr();
    gotoxy(13,17);
    textcolor(7);
    cprintf ("ANY KEY FOR GAME");
    gotoxy(14,19);
    textcolor(7);
    cprintf ("ESCAPE TO EXIT");

    gotoxy(3,24);
    textcolor(7);
    cprintf("POINTS    LIVES   POND FISH    HIGH");
    drawscore(); drawhiscore();

    drawtitle();

    gettime(&ti); s = ti.ti_sec / 3 - 3; a = 0; 

    while (!kbhit())
    {
        gettime(&ti);
        if (ti.ti_hund < 10 && s != ti.ti_sec / 3)
        {
            s = ti.ti_sec / 3; c = getrandom (6) + 9;
            gotoxy (12,13); textcolor(7);
            switch (a)
            {
                case 0:
                cprintf("PRESS IJKL TO TURN");
                break;

                case 1:
                cprintf("HOOK ");  textcolor(15);
                cprintf("%c",HOOK); textcolor(7);
                cprintf("  A DEAD EEL");
                break;

                case 2:
                cprintf("   FISH "); textcolor(c);
                cprintf("%c",224); textcolor(7);
                cprintf("  1000   ");
                break;

                case 3:
                cprintf("  MUDCRAB "); textcolor(c);
                cprintf("%c",42); textcolor(7);
                cprintf(" 3000   ");
                break;

                case 4:
                cprintf(" SWORDFISH "); textcolor(c);
                cprintf("%c",157); textcolor(7);
                cprintf(" 5000   ");
                break;
                
                case 5:
                cprintf(" THINFISH "); textcolor(c);
                cprintf("%c",174); textcolor(7);
                cprintf(" 10000  ");
                break;

                case 6:
                cprintf("SHELLFISH "); textcolor(c);
                cprintf("%c",239); textcolor(7);
                cprintf("  50000 ");
                break;

                case 7:
                cprintf(" SCHOOL "); textcolor(c);
                cprintf("%c",247); textcolor(7);
                cprintf("  100000 ");
                break;

                case 8:
                gotoxy (10,13);
                cprintf("EXTRA LIFE EACH 500000");
                break;

                case 9:
                gotoxy(10,13);
                cprintf("  BY SIMON BEER 1997  ");
                break;

            }
            a++; if (a == 10) a = 0;

        }
    }
    hit = getkey();
    clrtopscr();
}

void gameover (void)
{
    clrtopscr();
    gotoxy(16,13);
    textcolor(15);
    cprintf("GAME  OVER");
    wait(30); 
    clrtopscr();
}

void drawscreenboxes (void)
{
    int c;
    c = bordercol;
    drawborder (1,1,40,24,c,0);
    switch (screen % 25)
    {
        case 0:
        case 14:
        break;

        case 1:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);
        break;


        case 2:
        case 15:
        drawborder (13,9,29,16,c,1);
        break;

        case 3:
        case 16:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (13,7,17,10,c,1);
        drawborder (24,7,28,10,c,1);
        drawborder (13,15,17,18,c,1);
        drawborder (24,15,28,18,c,1);
        break;

        case 4:
        case 22:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);
        drawborder (12,9,29,16,c,1);
        break;

        case 5:
        drawborder (11,5,15,8,c,1);
        drawborder (26,5,30,8,c,1);
        drawborder (11,17,15,20,c,1);
        drawborder (26,17,30,20,c,1);

        drawborder (19,4,22,21,c,1);
        break;

        case 6:
        case 17:
        drawborder (5,4,18,6,c,1);
        drawborder (23,4,36,6,c,1);
        drawborder (5,19,18,21,c,1);
        drawborder (23,19,36,21,c,1);

        drawborder (10,9,13,16,c,1);
        drawborder (28,9,31,16,c,1);
        break;

        case 7:
        case 18:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (13,5,17,8,c,1);
        drawborder (24,5,28,8,c,1);
        drawborder (13,17,17,20,c,1);
        drawborder (24,17,28,20,c,1);
        
        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);
        break;

        case 8:
        drawborder (6,5,35,20,c,1);
        break;

        case 9:
        case 19:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (13,7,17,10,c,1);
        drawborder (24,7,28,10,c,1);
        drawborder (13,15,17,18,c,1);
        drawborder (24,15,28,18,c,1);
        
        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);
        drawborder (19,4,22,6,c,1);
        drawborder (19,19,22,21,c,1);
        break;

        case 11:
        case 20:
        drawborder (10,5,14,8,c,1);
        drawborder (27,5,31,8,c,1);
        drawborder (10,17,14,20,c,1);
        drawborder (27,17,31,20,c,1);

        drawborder (19,4,22,21,c,1);

        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);
        break;

        case 10:
        case 21:
        drawborder (5,4,18,6,c,1);
        drawborder (23,4,36,6,c,1);
        drawborder (5,19,18,21,c,1);
        drawborder (23,19,36,21,c,1);

        drawborder (10,9,13,16,c,1);
        drawborder (28,9,31,16,c,1);

        drawborder (19,11,22,14,c,1);
        break;


        case 12:
        case 23:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (13,5,17,8,c,1);
        drawborder (24,5,28,8,c,1);
        drawborder (13,17,17,20,c,1);
        drawborder (24,17,28,20,c,1);
        
        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);

        drawborder (19,11,22,14,c,1);
        break;

        default:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (12,7,16,10,c,1);
        drawborder (25,7,29,10,c,1);
        drawborder (12,15,16,18,c,1);
        drawborder (25,15,29,18,c,1);
        
        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);
        drawborder (19,4,22,6,c,1);
        drawborder (19,19,22,21,c,1);

        drawborder (18,11,23,14,c,1);
        break;
    }
}

void drawletter (int stx, int sty, int length)
{
    register int a;
    int b, dir;

    snakel = 99;  
    for (a=0;a<99;a++)
    {
        snakex[a] = stx;
        snakey[a] = sty;
    }
    
    if (length > 12) b=0;
    else b=21;

    a = b;
    length +=b;

    while (!kbhit() &&  a < length)
    {
        dir = titledata[a];
        movesnake(dir,1);
        wait(3);
        a++;
    }    
}

void drawtitle (void)
{
    
      drawletter (13,7,21);
      drawletter (20,7,21);
      drawletter (28,9,5);
}

void soundplay (void)
{
    if (lives < 1 || dead)
    {
        sound (0);
        return;
    }
    if (soundmessage == 0)
    {

        if (counter % 8 == 1) sound (33);
        if (counter % 8 == 2) sound (0);

        if (counter == 4) sound (36);
        if (counter == 5) sound (0);


        if (counter % 8 == 5  && counter != 5) sound (36);
        if (counter % 8 == 6  && counter != 6) sound (0);
    }
    else
    {
        sound(fishsong[soundmessage] / 2);
        soundmessage--;
    }

}

main()

{
    int a,seed;

    struct time ti;
    for (a=0;a<41;a++)
        sparestr[a] = 250;
    sparestr[41] = '\0';

    gettime (&ti);

    seed = ti.ti_hund * ti.ti_sec;
    srand(seed);

    gppconio_init();
    textmode (1);
    _setcursortype(0);
    
    titlescreen();

  while (hit != 27)
  {
    initgame();
    
    drawscreenboxes ();
    initlife(); drawscore(); drawlives(lives); drawsnake();
    playoctsong();
    do
    {
        if (lives < 3)
        {
            cleargamearea(0);
            initlife();
        
            drawsnake(); drawlives(lives);
            drawscore(); drawhiscore(); 
            wait(20);
        }
        
        wait(4); 
        
        drawfish(fish[currfish]);

        do
        {
            score++;
            drawscore();
            
            if (kbhit()) keyaction();
            movesnake(snakedir,0);
            soundplay();
            if (getrandom (100) < 3) drawhook();
            wait(speed);
        }
        while (!dead);
     sound (0);
     }
     while (lives);

    gameover();
    if (score > hiscore) hiscore = score;
    drawscore(); drawhiscore();
    titlescreen();
  }

    gotoxy (2,2);
    textcolor(7);
    _setcursortype(1); 
    textmode(3);
}

A few tweaks were needed to get the sound to work without sounding really awful. The main thing (really the only thing, but I tried many different options before I found this worked) was to slow down the CPU speed of the emulated machine a lot. The game it came with was 1000 MHz, much slower than the default, and I thought it sounded pretty slow, lol. Since that didn't work I gave up on that option for a while. But it was the right one.

Having the CPU speed at about 433 or less MHz works really well for the sound. I did this in the DOS Zone Studio, though it could also be done in the config file (but the studio was much faster to try different options out and then run them). I also tried some much slower settings just to see how it would go, and the sound was still good, though the game was clearly running slower with a CPU speed of much less than the 433, e.g. 133 was still quite playable but you could see it take longer to draw the screen borders and things like that.

It's amazing how we have got used to having such fast and powerful computer technology now, almost for free compared to the crazy high prices of really slow technology of the past few decades.

Byte.Yoga Homepage - Australian Cyber Security Web Magazine

Share This Page

If you liked this page, please share it with others! You can use the links to share on Facebook, Twitter, LinkedIn, Pinterest, and Email. Ther is also an RSS feed to get updates for the website.