[LINUX] Nostalgic, let's reproduce a character game like CBM-3032 with ncursesw.

With ncurses, the screen will be 80x24, so with ncursesw, using UTF-8 full-width code, with a square character, about 40 years ago of 40x24, PET, MZ-80, CBM-3032, I tried to rewrite the game of PC-8001 (which also has 80x25 mode). I modified the game "Raging Robots" a little and made it "Maneaters".

I will leave the detailed explanation of ncursesw to other articles (Oioi), but I will post the program list. Since there is no explanation of ncursesw, I wrote the comments as carefully as possible. You will see how to use minimal ncursesw.

In addition, the operation check was ubuntu18.04 and Debian stretch (9.6). Binary packages can be found on GitHub. https://github.com/fygar256/maneaters/blob/master/maneaters_1.3-1_amd64.deb

~~ Ubuntu libncursesw5 (6.1-1ubuntu1.18.04) and Debian libncursesw5 (6.0 + 20161126-1 + deb9u2) have bugs, even if you use vim, certain symbols in utf-8 will show It may be chipped or the character width may not be large enough. For example, ~~ "◯" (large circle: not a machine-dependent character, ambiguous width) causes a problem, but "O" (capital letter O, full-width) does not cause a problem. Regarding this, siracamus pointed out, "For Ubuntu terminals, it may be solved by changing" Ambiguous width character: "in the compatibility tab of the terminal's Preferences setting to" full-width ". So, when I tried it, it was certainly solved in the game (prototype).

~~ But vim didn't work properly. ~~ It seems that the East Asian Ambiguous font is displayed in half-width or full-width depending on the locale. Again, according to siracamus's information, vim worked fine with : set ambiwidth = double.

The terminal and vim settings have been resolved with the above two points. Thank you, siracamus.

This program is made without using ambiguous fonts.

Before compiling

sudo apt-get install libncursesw5-dev

Please install the package as. How to compile

cc maneaters.c -o maneaters -lncursesw

is.

maneaters.c


//
//Nostalgic, CBM-Character games like 3032
//Realized with ncursesw.
//  Maneaters. Ver 1.3
//

#include  <stdio.h>
#include  <stdlib.h>
#include  <time.h>

//To use ncursesw, the following two header files
#include  <ncursesw/ncurses.h>
#include  <locale.h>

//The characters to be output are defined as constants.
#define ENEMY   "O"
#define PLAYER  "@"
#define ROCK    "#"
#define CRASH   "X"
#define SPACE   " "

//Definition of a function that generates a random number from 0 to x.
#define rnd(x) (rand()%x)

int enemyl[40];   //Flag of whether the enemy is alive
int enemyx[40];   //Enemy x coordinate
int enemyy[40];   //Enemy y coordinate

int rockl[320];   //Flag of whether there is a rock
int rockx[320];   //Rock x coordinate
int rocky[320];   //Rock y coordinate

int enemymax=12;  //Number of first enemies

int rockmax=120;  //Number of first rocks

int playerx,playery;  //Player coordinates

int h,w;              //Screen height,width

int xsize,ysize;      //Game screen size.
                      //80x25 screen, square full-width UTF with monospaced font-8
                      //And I'm using it
                      //Here, xsize is 40.

int gamestat;         //Game status: 0 Now playing
                      //1 lose
                      //3 win

/*
 *When the player moves
 *Determining if you hit something
 *Return value;1: Collision 0: Nothing
 */
int check_player_crash() {
  //
  //Judgment of whether or not it hit the enemy
  //
  for(int i=0;i<enemymax;i++) {
    if((enemyl[i]==1)&&(playerx==enemyx[i]) && (playery==enemyy[i]))
      return(1);
    }
  //
  //Determining if you hit a rock
  //
  for(int i=0;i<rockmax;i++) {
    if((rockl[i]==1)&&(playerx==rockx[i]) && (playery==rocky[i]))
      return(1);
    }
  return(0);
}

/*
 *A function that displays characters on the screen.
 *Since it is handled in full-width units,
 *The x coordinate is doubled.
 */
int putchara(int x,int y,char *str){
  move(y,x*2);
  addstr(str);
}

/*
 *Game initialization
 */
int init_game() {
  int i;
  clear();            //Clear screen

  gamestat=0;         //Initialize game state

  //Take the seed of the random number from time and initialize it.
  srand((unsigned)time(NULL));

  //
  //Put a rock.
  //The 0th is to avoid collision between the player and the enemy
  //Place the dummy in the center of the screen. Therefore, the number of rocks is
  //  rockmax-It becomes 1.
  // 
  rockx[0]=xsize/2;
  rocky[0]=ysize/2;
  rockl[0]=0;
  for(i=1;i<rockmax;i++) {
    putchara(rockx[i]=rnd(xsize),rocky[i]=rnd(ysize),ROCK);
    rockl[i]=1;
    }

  //Place the enemy so that it does not hit the rock.
  i=0;
  while(i<enemymax) {
    int ex,ey,f;
    ex = rnd(xsize);
    ey = rnd(ysize);
    f = 0;
    for(int j=0;j<rockmax;j++) {
      if ((ex==rockx[j])&&(ey==rocky[j]))
        f=f|1;
      }
    if (f==0) {
      putchara(enemyx[i]=ex,enemyy[i]=ey,ENEMY);
      enemyl[i]=1;
      i++;
      }
    }

  //Place the player in the center of the screen.
  putchara(playerx=xsize/2,playery=ysize/2,PLAYER);

}

/*
 *Move the enemies
 */
void  move_maneaters() {
  int dx,dy;
  int x,y;

  for(int i=0;i<enemymax;i++) {
    if (enemyl[i]==0)   //Skip if the enemy is dead
          continue;
    //
    //Ask for the difference to chase the player
    //
    dx=0,dy=0;
    if (enemyx[i]<playerx) dx=1;
    if (enemyx[i]>playerx) dx=-1;
    if (enemyy[i]<playery) dy=1;
    if (enemyy[i]>playery) dy=-1;

    //Disappear the enemy at your current position
    putchara(enemyx[i],enemyy[i],SPACE);

    //Find the coordinates of where the enemy moved
    x=enemyx[i]+dx,y=enemyy[i]+dy;

    //
    //Check for collisions between enemies
    //
    for(int j=0;j<enemymax;j++) {
      if ((i!=j)&&(enemyl[j]==1) && (x==enemyx[j]) && (y==enemyy[j])) {
          enemyl[i]=0;          //Lower the flag that the enemy is alive.
          }
      }

    //
    //Collision check with rocks
    //
    for(int j=0;j<rockmax;j++) {
      if ((rockl[j]==1)&&(x==rockx[j]) && (y==rocky[j])) { //Did you hit the rock?
        rockl[j]=enemyl[i]=0;
        putchara(x,y,SPACE);  //Erase the rock
        }
      }

    enemyx[i]=x,enemyy[i]=y;  //Take the enemy one step further
    if (enemyl[i])    //Is the enemy alive?
      putchara(enemyx[i],enemyy[i],ENEMY);  //Draw the enemy

    //
    //Check if it collided with the player
    //
    if ((x==playerx) && (y==playery))
        gamestat=1;     //If you collide, you lose
    }

}

//
//Game main loop
//
int main_game() {
  int f,dx,dy;
  while(1) {

/*
 *Player processing
 */
  switch(getch()) {     //Player key input and acquisition of movement direction
    case  '7': dx=-1,dy=-1; break;
    case  '8': dx= 0,dy=-1; break;
    case  '9': dx= 1,dy=-1; break;
    case  'u': case  '4': dx=-1,dy=0; break;
    case  'i': case  '5': dx= 0,dy=0; break;
    case  'o': case  '6': dx= 1,dy=0; break;
    case  'j': case  '1': dx=-1,dy=1; break;
    case  'k': case  '2': dx= 0,dy=1; break;
    case  'l': case  '3': dx= 1,dy=1; break;
    default:
      continue;
    }

    //Does the player go off the screen?
    if (((playerx+dx)>=0) && ((playerx+dx)<xsize) &&
        ((playery+dy)>=0) && ((playery+dy)<ysize)) {

      putchara(playerx,playery,SPACE);  //Take the player one step further.
      playerx+=dx;
      playery+=dy;
      putchara(playerx,playery,PLAYER);

      //When the player hits a rock or an enemy, the game is over.
      if (check_player_crash()) {
              gamestat=1;
              return(0);
            }
      }
    /*
     *Enemy processing
     */
    move_maneaters();
    if (gamestat!=0) return(0);

    /*
     *Win if the man-eater is annihilated
     */
    f=0;
    for(int j=0;j<enemymax;j++)
      f|=enemyl[j];
    if (f==0) {
      gamestat=3;
      return(0);
      }
  }
}
    
/*
 *A function that asks if you want to play again
 *Return value; 0: No 1: Yes
 */
int tryagainp() {
  printw("Try Again? [y/n]");
  while(1) {
    switch(getch()) {
      case  'n': return(0);
      case  'y': return(1);
      default: continue;
      }
    }
}

/*
 *Game description screen
 *
 */
void  instruction() {
    clear();      //Clear screen

    printw("Maneaters Ver 1.3\n"); //Print to stdscr with printw.

    printw("Mission : kill all maneaters to survive!\n");
    printw("%s -- Maneater, chase player step by step.\n",ENEMY);
    printw("%s -- Rock, die maneaters and player when touched. \n",ROCK);
    printw("%s -- Player, control for maneaters to crash to rock and survive!\n",PLAYER);
    printw("\n");
    printw("Key control:             Tenkey:     \n");
    printw(" 7  8  9            7 8  9   \n");
    printw("  ↖ ↑  ↗                 ↖ ↑  ↗     \n");
    printw("u← i →o             4← 5→ 6   \n");
    printw("  ↙ ↓  ↘                 ↙ ↓  ↘     \n");
    printw(" j  k   l           1  2  3   \n");
    printw("\n");
    printw(" 'i' and '5' move maneater and don't move player.\n");
    printw("             Good Luck\n");
    printw("hit key\n");

    getch();    //Waiting for one-character key input.
  }

//
//Game description screen and main loop loop
//
void  play_game() {
  while(1) {

    instruction();    //Call the game description screen.

    init_game();      //Game initialization

    main_game();      //Game main loop

    if (gamestat==1) {  //Player loses
      putchara(playerx,playery,CRASH);
      move(0,0);
      printw("You Lose ");
      }

    if (gamestat==3) {  //Player wins
      move(0,0);
      printw("You Win!! ");
      }

    if (tryagainp()==0) return; //Return if not replayed.
    }
}
/*
 *main function
 */
int main() {
  setlocale( LC_ALL,"" ); //UTF with ncursesw-To use 8
                          // LC_Set ALL.

  initscr();              //Screen initialization

  noecho();               //Remove keyboard echo

  curs_set(0);            //Hide the cursor.

  getmaxyx(stdscr,h,w);   //Screen size, h,Store in w
                          //Usually 80x25

  xsize=w/2;              //Find the screen size to use in the game.

  ysize=h;                //Find the Y size of the screen used in the game.

  play_game();            //Run the game.

  endwin();               //Close the screen.

  exit(0);                //Finished.
}


Recommended Posts

Nostalgic, let's reproduce a character game like CBM-3032 with ncursesw.
Let's make a simple game with Python 3 and iPhone
Let's feel like a material researcher with machine learning
I made a puzzle game (like) with Tkinter in Python
Let's make a rock-paper-scissors game
Let's feel like a material researcher with python [Introduction to pymatgen]
Let's make a GUI with python.
Let's make a breakout with wxPython
Zura made like a life game
Let's make a graph with python! !!
Let's make a supercomputer with xCAT
Play like a web app with ipywidgets
I made a character counter with Python
Let's create a free group with Python
Let's scrape a dynamic site with Docker
Let's make a voice slowly with Python
Let's make a simple language with PLY 1
I made a life game with Numpy
Let's make a web framework with Python! (1)
Let's make a tic-tac-toe AI with Pylearn 2
Let's analyze a Chinese retro game machine
Read a character data file with numpy
Let's make a Twitter Bot with Python!
I made a roguelike game with Python
Let's make a web framework with Python! (2)