//
// approximate emulator of ZX80 ZPACE INVADERS assembler code
//
//  written for Unix ( uses real time  keyboard input getch()  )
//    gcc zpace.c                 ./a.out
//
//  keys 1 (move left)     2 (move right)      3 fire     
//
//

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <assert.h>

 
#define NSIR 5       // number of rows of SI (was 5   need 1 to NLS-2)
#define NSIC 10      // number of columns of SI (was 10  need 1 to NCS-2)

#define NLS  12      // number of lines on display (was 12  need > NSIR+1)
#define NCS  15      // width of display ( was  15    need > NSIC+1)
 
int main()
{
    int ifr;
    int y=7;
    int lc=7; int lr=-1;
    int bc=NSIC/2; int br=0;
    int nbc=NSIC/2; int nbr=0;
    int sr=0; int sc=0;
    int nsi;
    int ir,ic,ib,irow;
    int ichar;
    int si[NSIR][NSIC];       // store of SI (1 if on, 0 if dead)
    char dispf[NLS][NCS+1];      // display buffer
     

// SI initialise
     nsi=0;
     for(ir=0; ir<NSIR; ++ir)  
     { for (ic=0; ic<NSIC; ++ic){
       si[ir][ic]=1; ++nsi; }; } 

    system("clear");

// big loop to run game
    for (ifr=1;ifr<999999;++ifr)
    {

 // empty display buffer
     for(ir=0; ir<NLS; ++ir)
     { for (ic=0; ic<NCS; ++ic){ dispf[ir][ic]='.';} 
       dispf[ir][NCS]='\0' ;} 

 // evaluate new bomb position: from top NSIR rows of SI (if above bottom)

     if ( sr+NSIR > NLS-2) {irow= NLS-1-sr;} else {irow=NSIR;}
     ir= y+lr+ifr;                   // quasi-random bomb position
      nbr=sr;nbc=sc+NSIC/2;          // default (should not be needed)
     for (ic=0; ic < (irow*NSIC-1); ++ic)  {
      ib= (ir+ic) % (irow*NSIC) ;    // cycle round irow rows - take first
     if ( *(&si[0][0]+ib) == 1 ) 
         {nbc = (ib % NSIC)+sc; nbr= (ib/NSIC) +sr; break; }
      }


//----either move SI or move lby  & 31 is 1/32   & 15 is 1/16
      if( (ifr & 15 ) != 0 ) {

// ------ move lby-------------------------------------------


 //  a key press: -1 if no press else 1:49 2:50 etc (care -> are 3 char)
         ichar=getch(); 
 // set delay in tenths of second in getch()

// move y (You)
        if (ichar==49 && y > 0){y=y-1;}    //  "1"
        if (ichar==50 && y < NCS-1){y=y+1;}   //  "2"
                dispf[NLS-1][y]='Y';
// si at y? (should not occur)
        if ( y >= sc && y-sc <= NSIC-1 && NLS-sr <= NSIR ) {
        if ( si[NLS-sr-1][y-sc] == 1 ){ 
        printf("\n si lands on you \n");return(4);}
        }

//move b:  bomb    or new bomb if at bottom
        if (br > NLS-2 ) { br=nbr; bc=nbc;}
        else { br=br+1; dispf[br][bc]='*';}
// b at y ?
        if( (br == NLS-1) && (bc == y) ) {printf("\n Bombed \n"); return(2);}

// move l (Your laser)
        if (ichar==51 && lr < 0 ){lc=y; lr=NLS-1; }     // fire l  "3"
        if(lr > -1) { lr=lr-1;                          // l moves up
             if( lr >= 0) { dispf[lr][lc]='^';}  }

 // si at l ?   and set up display of SI
     for(ir=0; ir<NSIR; ++ir)
     { for (ic=0; ic<NSIC; ++ic){
      if( si[ir][ic]==1 ) {                  
        if( lr == ir+sr && lc == ic+sc) 
       {si[ir][ic]=0; lr=-1; --nsi; dispf[ir+sr][ic+sc]='W';} 
        else { dispf[ir+sr][ic+sc]='Z' ;}   ;}
         ; }; } 

             ;}
         else  {
 // move SI --------------------------------------------------------
      if ( (sr & 1 ) == 1){
 //                                          odd row 1 3
       if( sc > 0 ) {sc=sc-1; } else {sr=sr+1;} 
        ;}
      else {
 //                                         even row 0 2
       if( sc + NSIC < NCS ) {sc=sc+1; } else {sr=sr+1;}
        ;}
      
      for(ir=0; ir<NSIR; ++ir)
      { for (ic=0; ic<NSIC; ++ic){
      if( si[ir][ic]==1 ) {           // test SI reach bottom line
        if( ir+sr == NLS-1 ) {printf("\n SI on bottom \n"); return(3);}
         dispf[ir+sr][ic+sc]='Z'
             ;} ; }; } 

          ;}

//------------------ display -------------------------------------

          system("clear");

          for (ir=0; ir<NLS; ++ir){
          printf("\n |");
           for (ic=0; ic<NCS; ++ic){ printf("%c",dispf[ir][ic]);}
          printf("|");
          }
          printf("\n Number SI: %d \n",nsi);
          if (nsi == 0) { printf("\n all killed \n"); return(5);}
    }

     
    return (0);
}

//----------------------------------------------------------------
//
// reads 1 character directly - with timeout set (VTIME)
//
// memcpy  string.h     assert assert.h  debug mode

int getch(void) {
      int c=0;

      struct termios org_opts, new_opts;
      int res=0;
          //-----  store old settings -----------
      res=tcgetattr(STDIN_FILENO, &org_opts);
      assert(res==0);
          //---- set new terminal parms --------
      memcpy(&new_opts, &org_opts, sizeof(new_opts));
      new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
      new_opts.c_cc[VMIN]=0;                  // see man tcsetattr
      new_opts.c_cc[VTIME]=1;                 // delay in 1/10 of sec
      tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
      c=getchar();
          //------  restore old settings ---------
      res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
      assert(res==0);
      return(c);
}

