2010年11月20日土曜日

基本情報技術者

合格してました。
うれしいというよりホッとしたと言うのが正直な感想。
やっぱあれか、昼に食べた肉チャーハン定食が効いたのか?

とりあえず、次回何を受験するかはまだ決めていません。
候補としては、順当に応用情報技術者か、ちょい飛躍してネットワークスペシャリストあたりかな。
それに加えて情報セキュリティスペシャリストも取得できればまあ食いっぱぐれはなさそうだけど、
それぞれその難度がハンパないので多分スペシャリストレベルの複数取得は難しいでしょう。
俺の脳みその性能的に考えて。

まあネットワークスペシャリストに絞った方が無難かな。
個人的にもネットワーク技術に興味はあるし、
そもそもネットワーク技術者ってプログラマよりも希少だもんな。
まあ俗に言うハッカーと呼ばれる領域のプログラマの方がもっと希少だけど、
俺はおそらくもうその領域まではいけないと思う。
年齢と言うより、どちらかというとこれは資質の問題だと思う。

でもやっぱり凡庸なプログラマ程度の能力は欲しいし、プログラミングは好きだから、
多分プログラミングの勉強はやめることは無いと思う。
なりたいものになれないことと、その分野が好きということは別問題。
プロ野球選手になれなくても野球が大好きな人はたくさんいる。それと同じだ。
少なくとも個人的にはそう思っている。

まあなんにせよ、とりあえずIT分野で前に進む取っ掛かりはつかめた感触はあったかな。
30半ばを過ぎても、頑張ればなんとかなるもんですね。
だが本当の地獄はここからだ・・・!(byベジータ)

2010年10月16日土曜日

明日試験な訳ですが。

性懲りもなく明日の基本情報技術者試験に挑戦してきます。
前回が午後問で6点足りなくて落ちたものだからリベンジする気になったけど、
ここ数年の過去問を解いている今現在、かなり消耗している体たらくです。
全く、どうしたもんですかね。

まあでも、今の会社は自分よりも遙か先にいる人も歩みを止めず前に進み続ける人が多く、
当然こちらも前に進んでいかないと置いていかれるので、結構必死です。
でもそういう環境が心地いいのも事実で、少なくとも前を向いて仕事を出来ているのは
この会社に入ってからのような気がします。
まあ十数年社会人やっていて恥ずかしい話ではありますが。

多分、基本情報をパスしたら次は応用情報を受けるつもりでいます。
当然難易度はさらに跳ね上がるけど、挑戦しがいのある試験だと思うので頑張ります。

2010年9月12日日曜日

スネークゲーム上げてみた。

前回のソースを多少遊びやすくした程度のものを上げてみます。
ヒトバシラーな覚悟のある方はダウンロードしてみてね。
ダウンロード

画面イメージ

2010年9月11日土曜日

スネークゲームの雛形

DXライブラリでスネークゲームの雛形を作ってみました。
ぬるっと動かすことにこだわったら、こんな長くなっちゃった。
#include "dxlib.h"

// ウィンドウの大きさ
#define WINDOW_WIDTH  640
#define WINDOW_HEIGHT  480

// DXLIB関数の返り値をわかりやすく。
#define DX_FALSE   (-1)
#define DX_TRUE    0

// キャラクタの大きさとフィールドの大きさを決める。
#define CHAR_SIZE   16
#define FIELD_WIDTH   (WINDOW_WIDTH / CHAR_SIZE)
#define FIELD_HEIGHT  (WINDOW_HEIGHT / CHAR_SIZE)
#define FIELD_ARRAY   (FIELD_WIDTH * FIELD_HEIGHT)

// へびのパラメータ
#define SNAKE_LENGTH  5
#define SNAKE_MAX   256
#define SNAKE_SPEED   2
#define FOOD_NUM   100
#define ENEMY_NUM   30

// X,Y座標から一次元配列のインデックスを算出するマクロ
#define GET_INDEX(A,B)  ((A) + (B) * FIELD_WIDTH)

// キャラクタコードの列挙体
enum eCharCode{ CHR_BLANK,CHR_FOOD,CHR_ENEMY,CHR_SNAKEH,CHR_SNAKEB,CHR_END };

// キーコードの列挙体
enum eKeyCode{ KEY_UP,KEY_DOWN,KEY_RIGHT,KEY_LEFT,KEY_ESC };

// X,Y座標の増分を設定(4方向)
const int Vx[] = { 0, 0, 1,-1};
const int Vy[] = {-1, 1, 0, 0};

// へびのキャラクタパラメータを格納する構造体
typedef struct s_snake{
 int x;
 int y;
 int vx;
 int vy;
}SNAKEBODY;

// ゲーム全体のパラメータを格納する構造体
typedef struct s_global{
 SNAKEBODY sb[SNAKE_MAX];
 int score;
 int snake_length;
 int stage[FIELD_ARRAY];
 int color[CHR_END];
 int key_code;
}GLOBALS;

// 指定座標のキャラクタコードを取得
int GetChar(GLOBALS *g,int x,int y){
 return g->stage[GET_INDEX(x / CHAR_SIZE,y / CHAR_SIZE)];
}

// 指定座標に指定キャラクタを配置
void SetChar(GLOBALS *g,int x,int y,int c){
 g->stage[GET_INDEX(x / CHAR_SIZE,y / CHAR_SIZE)] = c;
}

// ゲームパラメータ初期化処理
void GameInit(GLOBALS *g){
 int i;
 //乱数初期化
 SRand(GetNowCount());
 //ステージ配列をブランクで埋める
 for(i=0;i<FIELD_ARRAY;i++) g->stage[i] = CHR_BLANK;
 //へび配列を初期化し、その情報を元にへびのキャラクタをステージ配列に配置する
 for(i=0;i<SNAKE_MAX;i++){
  if(i < SNAKE_LENGTH){
   const int x = (FIELD_WIDTH / 2) * CHAR_SIZE;
   const int y = (FIELD_HEIGHT / 2 + i) * CHAR_SIZE;
   g->sb[i].x = x;
   g->sb[i].y = y;
   g->sb[i].vx = 0;
   g->sb[i].vy = -1;
   if(i) SetChar(g,x,y,CHR_SNAKEB);
  }else{
   g->sb[i].x = 0;
   g->sb[i].y = 0;
   g->sb[i].vx = 0;
   g->sb[i].vy = 0;
  }
 }
 //色の設定(超適当)
 for(i=0;i<CHR_END;i++){
  g->color[i] = GetColor(
   255 - ((i % 2) * 200),
   255 - ((i % 3) * 100),
   255 - ((i % 5) * 50)
  );
 }
 //障害物を置く
 for(i=0;i<ENEMY_NUM;i++){
  int r = GetRand(FIELD_ARRAY - 1);
  if(g->stage[r] == CHR_BLANK) g->stage[r] = CHR_ENEMY;
  else i--;
 }
 //エサを置く
 for(i=0;i<FOOD_NUM;i++){
  int r = GetRand(FIELD_ARRAY - 1);
  if(g->stage[r] == CHR_BLANK) g->stage[r] = CHR_FOOD;
  else i--;
 }
 g->score = 0;
 g->snake_length = SNAKE_LENGTH;
 g->key_code = KEY_UP;
}

// へびの長さを伸ばす
void AddLength(GLOBALS *g){
 int l = g->snake_length;
 if(l == SNAKE_MAX) return;
 g->sb[l].x = g->sb[l - 1].x - g->sb[l - 1].vx * CHAR_SIZE; 
 g->sb[l].y = g->sb[l - 1].y - g->sb[l - 1].vy * CHAR_SIZE;
 g->sb[l].vx = g->sb[l - 1].vx; 
 g->sb[l].vy = g->sb[l - 1].vy; 
 g->snake_length++;
}

// 当たり判定処理
int Collision(GLOBALS *g){
 int *stg = g->stage;
 SNAKEBODY *s = g->sb;
 const int chr = GetChar(g,s[0].x,s[0].y);
 if(chr == CHR_FOOD) AddLength(g);
 else if((chr == CHR_ENEMY)||(chr == CHR_SNAKEB)) return DX_FALSE;
 return DX_TRUE;
}

// へびを動かす
int MoveSnake(GLOBALS *g){
 SNAKEBODY *s = g->sb;
 int i,l = g->snake_length - 1;
 //方向を決める
 for(i=(l-1);i>=0;i--){
  if((s[i].x % CHAR_SIZE == 0)&&(s[i].y % CHAR_SIZE == 0)){
   if((i + 1) == l) SetChar(g,s[l].x,s[l].y,CHR_BLANK);
   s[i + 1].vx = s[i].vx;
   s[i + 1].vy = s[i].vy;
   if(i == 0){
    if(g->key_code >= 0){
     if(((s[i].vx == 0)&&(g->key_code > KEY_DOWN))||
      ((s[i].vy == 0)&&(g->key_code < KEY_RIGHT))){
      s[i].vx = Vx[g->key_code];
      s[i].vy = Vy[g->key_code];
     }
    }
    if(Collision(g) == DX_FALSE) return DX_FALSE;
   }else SetChar(g,s[i].x,s[i].y,CHR_SNAKEB);
  }
 }
 //方向に沿って移動させる
 for(i=0;i<g->snake_length;i++){
  s[i].x += s[i].vx * SNAKE_SPEED;
  s[i].y += s[i].vy * SNAKE_SPEED;
  if(i == 0){
   if((s[i].x < 0)||
    (s[i].x > (WINDOW_WIDTH - CHAR_SIZE))||
    (s[i].y < 0)||(s[i].y > (WINDOW_HEIGHT - CHAR_SIZE)))
     return DX_FALSE;
  }
 }
 return DX_TRUE;
}

// キーコードを取得する
int GetKey(void){
 switch(GetJoypadInputState(DX_INPUT_KEY_PAD1)){
 case PAD_INPUT_DOWN:
  return KEY_DOWN;
 case PAD_INPUT_UP:
  return KEY_UP;
 case PAD_INPUT_RIGHT:
  return KEY_RIGHT;
 case PAD_INPUT_LEFT:
  return KEY_LEFT;
 case PAD_INPUT_START:
  return KEY_ESC;
 }
 return -1;
}

// ゲーム更新処理
int GameUpdate(GLOBALS *g){
 int k = GetKey();
 if(k >= 0){
  g->key_code = k;
  if(g->key_code == KEY_ESC) return DX_FALSE;
 }
 if(MoveSnake(g) == DX_FALSE) return DX_FALSE;
 return DX_TRUE; 
}

// ゲーム描画処理
void GameDraw(GLOBALS *g){
 int x,y,i;
 //背景描画
 //DrawBox(0,0,WINDOW_WIDTH,WINDOW_HEIGHT,0,TRUE);
 for(y=0;y<FIELD_HEIGHT;y++){
  const int yy = y * CHAR_SIZE;
  for(x=0;x<FIELD_WIDTH;x++){
   const int xx = x * CHAR_SIZE;
   const int col = g->color[GetChar(g,xx,yy)];
   DrawBox(xx,yy,xx + CHAR_SIZE,yy + CHAR_SIZE,col,TRUE);
  }
 }
 //へびの描画
 for(i=(g->snake_length - 1);i>= 0;i--){
  const int x = g->sb[i].x;
  const int y = g->sb[i].y;
  int col;
  if(i) col = g->color[CHR_SNAKEB];
  else col = g->color[CHR_SNAKEH];
  // 実際は4キャラ分しか描画してないんだぜ。
  if((i < 2)||(i >= g->snake_length - 2)){
   DrawBox(x,y,x + CHAR_SIZE,y + CHAR_SIZE,col,TRUE);
  }
 }
}

// Windowsアプリケーションエントリポイント
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
                               LPSTR lpCmdLine,int nCmdShow){
 GLOBALS g;
 ChangeWindowMode(TRUE);
 if(DxLib_Init() == DX_FALSE) return DX_FALSE;
 SetDrawScreen(DX_SCREEN_BACK);
 SetWaitVSyncFlag(TRUE);
 GameInit(&g);
 while(ProcessMessage() == DX_TRUE){
  if(GameUpdate(&g) == DX_FALSE) break;
  GameDraw(&g);
  ScreenFlip();
 }
 DxLib_End();
 return 0;
}

2010年9月5日日曜日

マインスイーパ移植したお!(Windows版)

Linux(Ubuntu)版をWindows版に移植しました。
っても、入出力まわりをちょこっと変えただけですが。
GUI版はすでにWindows標準のがあるので作る気はありません。
ビットマップの素材用意するのも結構大変だしねw
Windowsのコマンドプロンプト上だとエスケープシーケンスが使えないので、
Win32APIを使ってそこら辺を操作しています。
画面消去もコマンドプロンプト標準のclsコマンドを投げてるだけです。
何という手抜きw
// MineSweeper for Windows CUI
// Author:Imoimo
// Maked At:2010/08/29

// 各種インクルード
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <conio.h>   // DOS環境依存
#include <windows.h>  // Windows環境依存

// 定数定義
// ゲームパラメータ
#define STAGE_X     15
#define STAGE_Y     10
#define SCREEN_X    (STAGE_X + 2)
#define SCREEN_Y    (STAGE_Y + 2)
#define STAGE_ARRAY   (STAGE_X * STAGE_Y)
#define BOMB_NUM    20
#define GOVER_TIME   9999
#define MAX_STR_BUF   256
#define POLL_DELAY   16
#define SECOND_RACIO  500.0
// キーコード
#define KEY_NUM     8
#define KEY_SPACE    0x20
#define KEY_ENTER    0x0D
#define KEY_M     'Z'
#define KEY_UP     0x48
#define KEY_DOWN    0x50
#define KEY_LEFT    0x4B
#define KEY_RIGHT    0x4D
#define KEY_E     'E'
// 表示色設定
#define COL_BLACK  0
#define COL_WHITE  15
// 各種表示文字列
#define TITLE_STR    "-MineSweeper-"
#define HELP_STR    "[HELP]Arrow=Move,Z=Mark,Space=Flag,Enter=Open,E=Exit."
#define GOVER_STR    "Game Over!"
#define CLEAR_STR    "Game Clear!"
#define EXIT_STR    "Exit Game."

// キャラクタコードの列挙体
enum eCharCode{
 CHR_OPEN,
 CHR_1,
 CHR_2,
 CHR_3,
 CHR_4,
 CHR_5,
 CHR_6,
 CHR_7,
 CHR_8,
 CHR_FLAG,
 CHR_MARK,
 CHR_BOMB,
 CHR_CLOSE,
 CHR_CORNER,
 CHR_H_BORDER,
 CHR_V_BORDER,
};

// ゲームループ切り替えスイッチの列挙体
enum eGameMode{
 GAME_INIT,
 GAME_CLEAR,
 GAME_BODY,
 GAME_OVER,
 GAME_EXIT
};

// フラグと爆弾の一致判定をするかどうか
enum eFlagMode{ FLAG_TRUE,FLAG_FALSE };

// 表示キャラクタの配列
const char Chars[] = {
 ' ',
 '1','2','3','4','5','6','7','8',
 'P','X','*','#',
 '+','-','|'
};

// 表示顔文字の配列
const char *Faces[] = {"(--)","(^^)","(@@)","(xx)"};

// Windows環境依存・Win32API用
HANDLE hStdOut;

// ゲームパラメータ保存用グローバル変数
int Stage[STAGE_ARRAY];  //ステージの状態を保持
int Bombs[BOMB_NUM];  //爆弾の位置を保持
int X;      //カーソルの横座標
int Y;      //カーソルの縦座標
int TimeStart;    //ゲーム開始時のtime関数の値
int TimeCount;    //ゲーム開始からの経過秒数
int HitBombs;    //正解数のカウンタ
int FlagNum;    //旗の残り数
int GameCount;    //手数


// Windows環境依存・Win32API_Start

void setStdOutHandle(){
 hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
}

void releaseStdOutHandle(){
 CloseHandle(hStdOut);
}

void setReverse(){
 SetConsoleTextAttribute(hStdOut, COL_BLACK + (COL_WHITE << 4));
}

void setNormal(){
 SetConsoleTextAttribute(hStdOut, COL_WHITE + (COL_BLACK << 4));
}

// Windows環境依存・Win32API_End


// DOS環境依存・入出力処理_Start

// 画面消去
void cls(void){
 system("cls");
}

// getchラッパー関数(一定時間でループを抜ける。)
char getKey(void){
 char c = 0;
 int sec = 1 / (POLL_DELAY / SECOND_RACIO);
 while(sec-- > 0){
  Sleep(POLL_DELAY);
  if(kbhit()){
   c = getch();
   break;
  }
 }
 return c;
}

// DOS環境依存・キーボード入力処理_End


// 指定範囲(min〜max)の乱数値を返す。
int getRandom(int min,int max){
 return min + (int)(rand() * (max - min + 1.0) / (1.0 + RAND_MAX));
}

// 2次元座標から一次元配列のインデックスを返す
int getIndex(int x,int y){
 return x + y * STAGE_X;
}

// 配列インデックス値から2次元座標の横位置を返す
int getX(int index){
 return index % STAGE_X;
}

// 配列インデックス値から2次元座標の縦位置を返す
int getY(int index){
 return index / STAGE_X;
}

// ステージ初期化
void initStage(void){
 int i;
 for(i=0;i<STAGE_ARRAY;i++) Stage[i] = CHR_CLOSE;
 for(i=0;i<BOMB_NUM;i++) Bombs[i] = 0;
}

// 爆弾配置
void setBomb(){
 int i,j;
 srand(time(NULL));
 for(i=0;i<BOMB_NUM;i++){
  int bi,ind = getIndex(X,Y),dup = 0;
  bi = getRandom(0,STAGE_ARRAY - 1);
  if(bi == ind){
   dup = 1;
  }else{
   for(j=0;j<i;j++){
    if(Bombs[j] == bi) dup = 1;
   }
  }
  if(dup == 0) Bombs[i] = bi;
  else i--;
 }
}

// ゲームパラメータ初期化
int initGame(void){
 X = STAGE_X / 2;
 Y = STAGE_Y / 2;
 TimeStart = time(NULL);
 TimeCount = 0;
 HitBombs = 0;
 FlagNum = BOMB_NUM;
 GameCount = 0;
 initStage();
 return GAME_BODY;
}

// ゲームフィールドの横幅中央に文字列を表示する。
void drawCenter(const char *c){
 int len = strlen(c);
 int i,pad = (SCREEN_X - len) / 2;
 char cPads[SCREEN_X],cBuf[MAX_STR_BUF];
 strcpy(cBuf,c);
 for(i=0;i<SCREEN_X;i++) cPads[i] = ' ';
 if((pad + len) > SCREEN_X){
  cBuf[SCREEN_X - pad] = '¥0';
 }
 strcpy(cPads + pad,cBuf);
 puts(cPads);
}

// 枠の表示
void drawBorder(void){
 int i;
 for(i=0;i<SCREEN_X;i++){
  if((i == 0)||(i == STAGE_X + 1)) putchar(Chars[CHR_CORNER]);
  else putchar(Chars[CHR_H_BORDER]);
 }
 putchar('¥n');
}

// ステージの表示
void drawStage(void){
 int x,y;
 for(y=0;y<STAGE_Y;y++){
  putchar(Chars[CHR_V_BORDER]);
  for(x=0;x<STAGE_X;x++){
   if((x == X)&&(y == Y)) setReverse();
   putchar(Chars[Stage[getIndex(x,y)]]);
   setNormal();
  }
  putchar(Chars[CHR_V_BORDER]);
  putchar('¥n');
 }
}

// 画面全体の表示
void drawScreen(int face){
 cls();
 drawCenter(TITLE_STR);
 drawCenter(Faces[face]);
 drawBorder();
 drawStage();
 drawBorder();
 printf("Time : %04d Secs¥n",TimeCount);
 printf("Count: %04d Times¥n",GameCount);
 printf("Flag : %03d/%03d¥n",FlagNum,BOMB_NUM);
 puts(HELP_STR);
}

// ヒントの取得
int getHint(int x,int y,int sw){
 int i,count = CHR_OPEN;
 for(i=0;i<BOMB_NUM;i++){
  const int bx = getX(Bombs[i]);
  const int by = getY(Bombs[i]);
  if(Bombs[i] == getIndex(x,y)) return CHR_BOMB;
  else if((bx >= (x - 1))&&(bx <= (x + 1))
     &&(by >= (y - 1))&&(by <= (y + 1))){
   if((sw)||(Stage[getIndex(bx,by)] != CHR_FLAG)) count++;
  }
 }
 return count;
}

// ヒントのないセルを自動的に開く
void autoOpen(const int x,const int y){
 int i;
 const int vx[] = { 0, 0,-1, 1,-1,-1, 1, 1};
 const int vy[] = {-1, 1, 0, 0,-1, 1,-1, 1};
 for(i=0;i<8;i++){
  int *stg;
  const int xx = x + vx[i];
  const int yy = y + vy[i];
  if((xx >= 0)&&(xx < STAGE_X)&&(yy >= 0)&&(yy < STAGE_Y)){
   stg = &Stage[getIndex(xx,yy)];
   if(*stg == CHR_CLOSE){
    *stg = getHint(xx,yy,FLAG_FALSE);
    if(*stg == CHR_OPEN) autoOpen(xx,yy);
   }
  }
 }
}

// ヒントを中心とした8マスにマークされていない爆弾がなければ周囲の8マスを開く
void nearOpen(){
 int *stg = &Stage[getIndex(X,Y)];
 if((*stg == CHR_OPEN)||(*stg > CHR_8)) return;
 if(getHint(X,Y,FLAG_TRUE) == 0) autoOpen(X,Y);
}

// 旗を置く
int putFlag(void){
 int *stg = &Stage[getIndex(X,Y)];
 if((*stg == CHR_CLOSE)&&(FlagNum > 0)){
  *stg = CHR_FLAG;
  FlagNum--;
  if(getHint(X,Y,FLAG_FALSE) == CHR_BOMB){
   HitBombs++;
   if(HitBombs >= BOMB_NUM) return GAME_CLEAR;
  }
 }else if(*stg == CHR_FLAG){
  *stg = CHR_CLOSE;
  FlagNum++;
  if(getHint(X,Y,FLAG_FALSE) == CHR_BOMB) HitBombs--;
 }
 return GAME_BODY;
}

// 目印を置く
int putMark(void){
 int *stg = &Stage[getIndex(X,Y)];
 if(*stg == CHR_CLOSE) *stg = CHR_MARK;
 else if(*stg == CHR_MARK) *stg = CHR_CLOSE;
 return GAME_BODY;
}

// 隠されているセルを開く
int openCell(void){
 int *stg = &Stage[getIndex(X,Y)];
 if(*stg == CHR_CLOSE){
  if(GameCount++ == 0) setBomb();
  *stg = getHint(X,Y,FLAG_FALSE);
  if(*stg == CHR_OPEN) autoOpen(X,Y);
  else if(*stg == CHR_BOMB) return GAME_OVER;
 }else{
  nearOpen();
 }
 return GAME_BODY;
}

// カーソルを上に移動
int moveUp(void){
 if(Y > 0) Y--;
 return GAME_BODY;
}

// カーソルを下に移動
int moveDown(void){
 if(Y < (STAGE_Y - 1)) Y++;
 return GAME_BODY;
}

// カーソルを左に移動
int moveLeft(void){
 if(X > 0) X--;
 return GAME_BODY;
}

// カーソルを右に移動
int moveRight(void){
 if(X < (STAGE_X - 1)) X++;
 return GAME_BODY;
}

// ゲームを終了する。
int exitGame(void){
 puts(EXIT_STR);
 return GAME_EXIT;
}

// ゲーム更新処理
int updateGame(void){
 char c = getKey();
 const char keyCode[] = {
  KEY_SPACE,
  KEY_ENTER,
  KEY_M,
  KEY_UP,
  KEY_DOWN,
  KEY_LEFT,
  KEY_RIGHT,
  KEY_E
 };
 int (*f[])(void) ={
  putFlag,
  openCell,
  putMark,
  moveUp,
  moveDown,
  moveLeft,
  moveRight,
  exitGame
 };
 int i;
 TimeCount = time(NULL) - TimeStart;
 if(TimeCount >= GOVER_TIME) return GAME_OVER;
 for(i=0;i<KEY_NUM;i++){
  if(toupper(c) == keyCode[i]) return f[i]();
 }
 return GAME_BODY;
}

// ゲームメインループ
int gameBody(void){
 drawScreen(GAME_BODY);
 return updateGame();
}

// ゲームクリア時の処理
int gameClear(void){
 drawScreen(GAME_CLEAR);
 puts(CLEAR_STR);
 getch();
 return GAME_EXIT;
}

// 全部の爆弾を表示する
void displayAllBombs(){
 int i;
 for(i=0;i<BOMB_NUM;i++) Stage[Bombs[i]] = CHR_BOMB;
}

// ゲームオーバー時の処理
int gameOver(void){
 displayAllBombs();
 drawScreen(GAME_OVER);
 puts(GOVER_STR);
 getch();
 return GAME_EXIT;
}

// メイン関数(エントリポイント)
int main(void){
 int gm = GAME_INIT;
 int (*f[])(void) = {initGame,gameClear,gameBody,gameOver};
 setStdOutHandle();
 while(gm != GAME_EXIT) gm = f[gm]();
 releaseStdOutHandle();
 return 0;

}
実行イメージ

2010年9月2日木曜日

マインスイーパできたお!(Linux版)

とりあえずゲームとしての体裁が出来上がりました。
コーディングの際アドバイスをくれた職場の人に感謝!
// MineSweeper
// Author:Imoimo
// Maked At:2010/08/29

// 各種インクルード
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <termios.h> //Linux環境依存
#include <unistd.h>  //Linux環境依存

// 定数定義
// ゲームパラメータ
#define STAGE_X   15
#define STAGE_Y   10
#define SCREEN_X  (STAGE_X + 2)
#define SCREEN_Y  (STAGE_Y + 2)
#define STAGE_ARRAY  (STAGE_X * STAGE_Y)
#define BOMB_NUM  20
#define GOVER_TIME  9999
#define MAX_STR_BUF  256
#define POLL_DELAY  16667
#define SECOND_RACIO 100000.0
// キーコード
#define KEY_NUM   8
#define KEY_SPACE  0x20
#define KEY_ENTER  0x0A
#define KEY_M   'M'
#define KEY_UP   0x41
#define KEY_DOWN  0x42
#define KEY_LEFT  0x44
#define KEY_RIGHT  0x43
#define KEY_E   'E'
// エスケープシーケンスコード
#define ESC_CLEAR  "\x1b[2J\x1b[0;0H"
#define ESC_NORMAL  "\x1b[0m"
#define ESC_REVERSE  "\x1b[7m"
// 各種表示文字列
#define TITLE_STR  "-MineSweeper-"
#define HELP_STR  "[HELP]Arrow=Move,M=Mark,Space=Flag,Enter=Open,E=Exit."
#define GOVER_STR  "Game Over!"
#define CLEAR_STR  "Game Clear!"
#define EXIT_STR  "Exit Game."

// キャラクタコードの列挙体
enum eCharCode{
 CHR_OPEN,
 CHR_1,
 CHR_2,
 CHR_3,
 CHR_4,
 CHR_5,
 CHR_6,
 CHR_7,
 CHR_8,
 CHR_FLAG,
 CHR_MARK,
 CHR_BOMB,
 CHR_CLOSE,
 CHR_CORNER,
 CHR_H_BORDER,
 CHR_V_BORDER,
};

// ゲームループ切り替えスイッチの列挙体
enum eGameMode{
 GAME_INIT,
 GAME_CLEAR,
 GAME_BODY,
 GAME_OVER,
 GAME_EXIT
};

// フラグと爆弾の一致判定をするかどうか
enum eFlagMode{ FLAG_TRUE,FLAG_FALSE };

// Linux環境依存・keybd()、getch()関数の代替処理用
// デフォルトのターミナルパラメータを保存する構造体
static struct termios TOrigin;

// 表示キャラクタの配列
const char Chars[] = {
 ' ',
 '1','2','3','4','5','6','7','8',
 'P','X','*','#',
 '+','-','|'
};

// 表示顔文字の配列
const char *Faces[] = {"(--)","(^^)","(@@)","(xx)"};

// ゲームパラメータ保存用グローバル変数
int Stage[STAGE_ARRAY];  //ステージの状態を保持
int Bombs[BOMB_NUM];  //爆弾の位置を保持
int X;      //カーソルの横座標
int Y;      //カーソルの縦座標
int TimeStart;    //ゲーム開始時のtime関数の値
int TimeCount;    //ゲーム開始からの経過秒数
int HitBombs;    //正解数のカウンタ
int FlagNum;    //旗の残り数
int GameCount;    //手数


// Linux環境依存・keybd()、getch()関数の代替処理Start

// ターミナルパラメータの設定
void begin_getch(void){
  struct termios t;
  tcgetattr(0, &t);
  TOrigin = t;
  t.c_lflag &= ~(ICANON | ECHO);
  t.c_cc[VMIN] = 0;
  t.c_cc[VTIME] = 0;
  tcsetattr(0, TCSANOW, &t);
}

// ターミナルパラメータの復元
void end_getch(void) {
  tcsetattr(0, TCSADRAIN, &TOrigin);
}

// getch代替関数本体(keybd関数の機能も内包・一定時間でループを抜ける。)
char getch(void){
 char c = 0;
 int sec = 1 / (POLL_DELAY / SECOND_RACIO);
 begin_getch();
   while(1) {
    usleep(POLL_DELAY);
    if((sec-- <= 0)||(read(0, &c, 1) != 0)) break;
   }
 end_getch();
 return c;
}
// Linux環境依存・keybd()、getch()関数の代替処理用End


// 指定範囲(min~max)の乱数値を返す。
int getRandom(int min,int max){
 return min + (int)(rand() * (max - min + 1.0) / (1.0 + RAND_MAX));
}

// 2次元座標から一次元配列のインデックスを返す
int getIndex(int x,int y){
 return x + y * STAGE_X;
}

// 配列インデックス値から2次元座標の横位置を返す
int getX(int index){
 return index % STAGE_X;
}

// 配列インデックス値から2次元座標の縦位置を返す
int getY(int index){
 return index / STAGE_X;
}

// ステージ初期化
void initStage(void){
 int i;
 for(i=0;i<STAGE_ARRAY;i++) Stage[i] = CHR_CLOSE;
 for(i=0;i<BOMB_NUM;i++) Bombs[i] = 0;
}

// 爆弾配置
void setBomb(){
 int i,j;
 srand(time(NULL));
 for(i=0;i<BOMB_NUM;i++){
  int bi,ind = getIndex(X,Y),dup = 0;
  bi = getRandom(0,STAGE_ARRAY - 1);
  if(bi == ind){
   dup = 1;
  }else{
   for(j=0;j<i;j++){
    if(Bombs[j] == bi) dup = 1;
   }
  }
  if(dup == 0) Bombs[i] = bi;
  else i--;
 }
}

// ゲームパラメータ初期化
int initGame(void){
 X = STAGE_X / 2;
 Y = STAGE_Y / 2;
 TimeStart = time(NULL);
 TimeCount = 0;
 HitBombs = 0;
 FlagNum = BOMB_NUM;
 GameCount = 0;
 initStage();
 return GAME_BODY;
}

// ゲームフィールドの横幅中央に文字列を表示する。
void drawCenter(const char *c){
 int len = strlen(c);
 int i,pad = (SCREEN_X - len) / 2;
 char cPads[SCREEN_X],cBuf[MAX_STR_BUF];
 strcpy(cBuf,c);
 for(i=0;i<SCREEN_X;i++) cPads[i] = ' ';
 if((pad + len) > SCREEN_X){
  cBuf[SCREEN_X - pad] = '\0';
 }
 strcpy(cPads + pad,cBuf);
 puts(cPads);
}

// 枠の表示
void drawBorder(void){
 int i;
 for(i=0;i<SCREEN_X;i++){
  if((i == 0)||(i == STAGE_X + 1)) putchar(Chars[CHR_CORNER]);
  else putchar(Chars[CHR_H_BORDER]);
 }
 putchar('\n');
}

// ステージの表示
void drawStage(void){
 int x,y;
 for(y=0;y<STAGE_Y;y++){
  putchar(Chars[CHR_V_BORDER]);
  for(x=0;x<STAGE_X;x++){
   if((x == X)&&(y == Y)) printf(ESC_REVERSE);
   putchar(Chars[Stage[getIndex(x,y)]]);
   printf(ESC_NORMAL);
  }
  putchar(Chars[CHR_V_BORDER]);
  putchar('\n');
 }
}

// 画面全体の表示
void drawScreen(int face){
 printf(ESC_CLEAR);
 drawCenter(TITLE_STR);
 drawCenter(Faces[face]);
 drawBorder();
 drawStage();
 drawBorder();
 printf("Time : %04d Secs\n",TimeCount);
 printf("Count: %04d Times\n",GameCount);
 printf("Flag : %03d/%03d\n",FlagNum,BOMB_NUM);
 puts(HELP_STR);
}

// ヒントの取得
int getHint(int x,int y,int sw){
 int i,count = CHR_OPEN;
 for(i=0;i<BOMB_NUM;i++){
  const int bx = getX(Bombs[i]);
  const int by = getY(Bombs[i]);
  if(Bombs[i] == getIndex(x,y)) return CHR_BOMB;
  else if((bx >= (x - 1))&&(bx <= (x + 1))
     &&(by >= (y - 1))&&(by <= (y + 1))){
   if((sw)||(Stage[getIndex(bx,by)] != CHR_FLAG)) count++;
  }
 }
 return count;
}

// ヒントのないセルを自動的に開く
void autoOpen(const int x,const int y){
 int i;
 const int vx[] = { 0, 0,-1, 1,-1,-1, 1, 1};
 const int vy[] = {-1, 1, 0, 0,-1, 1,-1, 1};
 for(i=0;i<8;i++){
  int *stg;
  const int xx = x + vx[i];
  const int yy = y + vy[i];
  if((xx >= 0)&&(xx < STAGE_X)&&(yy >= 0)&&(yy < STAGE_Y)){
   stg = &Stage[getIndex(xx,yy)];
   if(*stg == CHR_CLOSE){
    *stg = getHint(xx,yy,FLAG_FALSE);
    if(*stg == CHR_OPEN) autoOpen(xx,yy);
   }
  }
 }
}

// ヒントを中心とした8マスにマークされていない爆弾がなければ周囲の8マスを開く
void nearOpen(){
 int *stg = &Stage[getIndex(X,Y)];
 if((*stg == CHR_OPEN)||(*stg > CHR_8)) return;
 if(getHint(X,Y,FLAG_TRUE) == 0) autoOpen(X,Y);
}

// 旗を置く
int putFlag(void){
 int *stg = &Stage[getIndex(X,Y)];
 if((*stg == CHR_CLOSE)&&(FlagNum > 0)){
  *stg = CHR_FLAG;
  FlagNum--;
  if(getHint(X,Y,FLAG_FALSE) == CHR_BOMB){
   HitBombs++;
   if(HitBombs >= BOMB_NUM) return GAME_CLEAR;
  }
 }else if(*stg == CHR_FLAG){
  *stg = CHR_CLOSE;
  FlagNum++;
  if(getHint(X,Y,FLAG_FALSE) == CHR_BOMB) HitBombs--;
 }
 return GAME_BODY;
}

// 目印を置く
int putMark(void){
 int *stg = &Stage[getIndex(X,Y)];
 if(*stg == CHR_CLOSE) *stg = CHR_MARK;
 else if(*stg == CHR_MARK) *stg = CHR_CLOSE;
 return GAME_BODY;
}

// 隠されているセルを開く
int openCell(void){
 int *stg = &Stage[getIndex(X,Y)];
 if(*stg == CHR_CLOSE){
  if(GameCount++ == 0) setBomb();
  *stg = getHint(X,Y,FLAG_FALSE);
  if(*stg == CHR_OPEN) autoOpen(X,Y);
  else if(*stg == CHR_BOMB) return GAME_OVER;
 }else{
  nearOpen();
 }
 return GAME_BODY;
}

// カーソルを上に移動
int moveUp(void){
 if(Y > 0) Y--;
 return GAME_BODY;
}

// カーソルを下に移動
int moveDown(void){
 if(Y < (STAGE_Y - 1)) Y++;
 return GAME_BODY;
}

// カーソルを左に移動
int moveLeft(void){
 if(X > 0) X--;
 return GAME_BODY;
}


// カーソルを右に移動
int moveRight(void){
 if(X < (STAGE_X - 1)) X++;
 return GAME_BODY;
}

// ゲームを終了する。
int exitGame(){
 puts(EXIT_STR);
 return GAME_EXIT;
}

// ゲーム更新処理
int updateGame(void){
 char c = getch();
 const char keyCode[] = {
  KEY_SPACE,
  KEY_ENTER,
  KEY_M,
  KEY_UP,
  KEY_DOWN,
  KEY_LEFT,
  KEY_RIGHT,
  KEY_E
 };
 int (*f[])(void) ={ 
  putFlag,
  openCell,
  putMark,
  moveUp,
  moveDown,
  moveLeft,
  moveRight,
  exitGame
 };
 int i;
 TimeCount = time(NULL) - TimeStart;
 if(TimeCount >= GOVER_TIME) return GAME_OVER;
 for(i=0;i<KEY_NUM;i++){
  if(toupper(c) == keyCode[i]) return f[i]();
 }
 return GAME_BODY;
}

// ゲームメインループ
int gameBody(void){
 drawScreen(GAME_BODY);
 return updateGame();
}

// ゲームクリア時の処理
int gameClear(void){
 drawScreen(GAME_CLEAR);
 puts(CLEAR_STR);
 getch();
 return GAME_EXIT;
}

// 全部の爆弾を表示する
void displayAllBombs(){
 int i;
 for(i=0;i<BOMB_NUM;i++) Stage[Bombs[i]] = CHR_BOMB;
}

// ゲームオーバー時の処理
int gameOver(void){
 displayAllBombs();
 drawScreen(GAME_OVER);
 puts(GOVER_STR);
 getch();
 return GAME_EXIT;
}

// メイン関数(エントリポイント)
int main(void){
 int gm = GAME_INIT;
 int (*f[])(void) = {initGame,gameClear,gameBody,gameOver};
 while(gm != GAME_EXIT) gm = f[gm]();
 return 0;
}
実行イメージ

2010年8月29日日曜日

いまどきCUIでマインスイーパ For Linux

あまりにも暇なのでLinux環境でCUIのマインスイーパ作ってみたw
まだUI部分だけの未完成品だけど、完成したら更に100行ぐらい増えるかも。
// MineSweeper
// Author:Imoimo
// Maked At:2010/08/29

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <termios.h> //Linux系環境依存
#include <unistd.h>     //Linux系環境依存

#define STAGE_X   20
#define STAGE_Y   10
#define SCREEN_X  (STAGE_X + 2)
#define SCREEN_Y  (STAGE_Y + 2)
#define STAGE_ARRAY  (STAGE_X * STAGE_Y)
#define BOMB_NUM  10
#define KEY_SPACE  0x20
#define KEY_ENTER  0x0A
#define KEY_LM   'm'
#define KEY_UM   'M'
#define KEY_UP   0x41
#define KEY_DOWN  0x42
#define KEY_LEFT  0x44
#define KEY_RIGHT  0x43
#define KEY_LE   'e'
#define KEY_UE   'E'
#define ESC_CLEAR  "\x1b[2J\x1b[0;0H"
#define ESC_NORMAL  "\x1b[0m"
#define ESC_REVERSE  "\x1b[7m"
#define TITLE_STR  "-MineSweeper-"
#define MAX_STR_BUF  256
#define POLL_DELAY  16667
#define HELP_STR  "[HELP]Arrow=MoveCursor,M=Mark,Space=Flag,Enter=OpenCell."

// キャラクタコード
enum eCharCode{
 CHR_OPEN,
 CHR_1,
 CHR_2,
 CHR_3,
 CHR_4,
 CHR_5,
 CHR_6,
 CHR_7,
 CHR_8,
 CHR_FLAG,
 CHR_MARK,
 CHR_BOMB,
 CHR_CLOSE,
 CHR_CORNER,
 CHR_H_BORDER,
 CHR_V_BORDER,
};
// ゲームモード
enum eGameMode{ GAME_INIT,GAME_CLEAR,GAME_BODY,GAME_OVER,GAME_EXIT };
// ターミナルパラメータ格納用(Linux依存)
static struct termios TOrigin;
// 表示キャラクタ配列
const char Chars[] = {
 '_','1','2','3','4','5','6','7','8','P','X','*','#','+','-','|'
};
// 表示顔文字配列(笑
const char *Faces[] = {"(--)","(^^)","(@@)","(xx)"};

// 色々とグローバル変数。
int Stage[STAGE_ARRAY];
int Bombs[BOMB_NUM];
int X;
int Y;
int TimeCount;
int HitBombs;
int FlagNum;
int FaceCode;

// 範囲内のランダム値を返す。
int getRandom(int min,int max){
 srand(time(NULL));
 return min + (int)(rand() * (max - min + 1.0) / (1.0 + RAND_MAX));
}

// 2次元座標から一次元配列の添字に変換する。
int getIndex(int x,int y){
 return x + y * STAGE_X;
}

// ステージ初期化。
void initStage(void){
 int i;
 for(i=0;i<STAGE_ARRAY;i++) Stage[i] = CHR_CLOSE;
 for(i=0;i<BOMB_NUM;i++){
  Bombs[i] = 0;
 }
}

// 地雷配置。
void setBomb(int x,int y){
 int i;
 for(i=0;i<BOMB_NUM;i++){
  int bi,ind = getIndex(x,y);
  bi = getRandom(0,STAGE_ARRAY);
  if(bi == ind){
   i--;
   continue;
  }
  Bombs[i] = bi;
 }
}

// ゲーム初期化。
int initGame(void){
 X = STAGE_X / 2;
 Y = STAGE_Y / 2;
 TimeCount = 0;
 HitBombs = 0;
 FaceCode = GAME_BODY;
 FlagNum = BOMB_NUM;
 initStage();
 return GAME_BODY;
}

// 文字列をフィールドの中央に表示する。
void drawCenter(const char *c){
 int len = strlen(c);
 int i,pad = (SCREEN_X - len) / 2;
 char cPads[SCREEN_X],cBuf[MAX_STR_BUF];
 strcpy(cBuf,c);
 for(i=0;i<SCREEN_X;i++) cPads[i] = ' ';
 if((pad + len) > SCREEN_X){
  cBuf[SCREEN_X - pad] = '\0';
 }
 strcpy(cPads + pad,cBuf);
 puts(cPads);
}

// ステージを描画。
void drawStage(void){
 int x,y;
 for(y=0;y<STAGE_Y;y++){
  putchar(Chars[CHR_V_BORDER]);
  for(x=0;x<STAGE_X;x++){
   if((x == X)&&(y == Y)) printf(ESC_REVERSE);
   putchar(Chars[Stage[getIndex(x,y)]]);
   printf(ESC_NORMAL);
  }
  putchar(Chars[CHR_V_BORDER]);
  putchar('\n');
 }
}

// 画面全体を描画。
void drawScreen(void){
 int i;
 printf(ESC_CLEAR);
 drawCenter(TITLE_STR);
 drawCenter(Faces[FaceCode]);
 for(i=0;i<SCREEN_X;i++){
  if((i == 0)||(i == STAGE_X + 1)) putchar(Chars[CHR_CORNER]);
  else putchar(Chars[CHR_H_BORDER]);
 }
 putchar('\n');
 drawStage();
 for(i=0;i<SCREEN_X;i++){
  if((i == 0)||(i == SCREEN_X - 1)) putchar(Chars[CHR_CORNER]);
  else putchar(Chars[CHR_H_BORDER]);
 }
 putchar('\n');
 printf("Time: %04d Secs\n",TimeCount);
 printf("Flag: %03d/%03d\n",FlagNum,BOMB_NUM);
 printf("Hit Bombs: %03d/%03d\n",HitBombs,BOMB_NUM);
}

// keybd()関数の代替:初期化
void begin_getch(void){
 struct termios t;
 tcgetattr(0, &t);
 TOrigin = t;
 t.c_lflag &= ~(ICANON | ECHO);
 t.c_cc[VMIN] = 0;
 t.c_cc[VTIME] = 0;
 tcsetattr(0, TCSANOW, &t);
}

// keybd()関数の代替:後始末
void end_getch(void) {
 tcsetattr(0, TCSADRAIN, &TOrigin);
}

// keybd()関数の代替:本体。
char getch(void){
 char c;
 begin_getch();
   while(1) {
    usleep(POLL_DELAY);
     if (read(0, &c, 1) != 0) break;
   }
 end_getch();
 return c;
}

// キー入力処理
int inputMethod(void){
 char c = getch();
 switch(c){
  case KEY_SPACE:
   if((Stage[getIndex(X,Y)] == CHR_CLOSE)&&(FlagNum > 0)){
    Stage[getIndex(X,Y)] = CHR_FLAG;
    FlagNum--;
   }else if(Stage[getIndex(X,Y)] == CHR_FLAG){
    Stage[getIndex(X,Y)] = CHR_CLOSE;
    FlagNum++;
   }
   break;
  case KEY_ENTER:
   if(Stage[getIndex(X,Y)] == CHR_CLOSE){
    Stage[getIndex(X,Y)] = CHR_OPEN;
   }
   break;
  case KEY_LM:
  case KEY_UM:
   if(Stage[getIndex(X,Y)] == CHR_CLOSE){
    Stage[getIndex(X,Y)] = CHR_MARK;
   }else if(Stage[getIndex(X,Y)] == CHR_MARK){
    Stage[getIndex(X,Y)] = CHR_CLOSE;
   }
   break;
  case KEY_UP:
   if(Y > 0) Y--;
   break;
  case KEY_DOWN:
   if(Y < STAGE_Y - 1) Y++;
   break;
  case KEY_LEFT:
   if(X > 0) X--;
   break;
  case KEY_RIGHT:
   if(X < STAGE_X - 1) X++;
   break;
  case KEY_LE:
  case KEY_UE:
   return 1;
   break;
  default:
   puts(HELP_STR);
   getch();
   break;
 }
 return 0;
}

// ゲーム更新処理
int updateGame(void){
 return 0;
}

// ゲームメインループ
int gameBody(void){
 drawScreen();
 if(inputMethod()) return GAME_EXIT;
 if(updateGame()) return GAME_OVER;
 return GAME_BODY;
}

// ゲームクリアループ
int gameClear(void){
 return GAME_EXIT;
}

// ゲームオーバーループ
int gameOver(void){
 return GAME_EXIT;
}

// エントリポイント。
int main(void){
 int gm = GAME_INIT;
 int (*f[])(void) = {initGame,gameClear,gameBody,gameOver};
 while(gm != GAME_EXIT) gm = f[gm]();
 return 0;
}

2010年8月28日土曜日

C言語でリスト構造その2。

前回のソースを改変し、配列的な使い方ができるようにしました。
まあだいたいこんなイメージだと思います。
ちなみにgccで動作確認済みです。おそらくVC++でも動くと思います。
// リスト構造を配列っぽく使えるようにする。
// ついでに要素の削除、挿入処理も追加してみる。
// 基本実装のおさらいが主旨なので、例外処理はなるべく省略したよ!

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

// リスト要素の構造体定義。
typedef struct s_list{
 int index;
 int value;
 struct s_list *next;
 struct s_list *prev;
}LIST;

// 構造体LIST型のグローバルポインタ変数を宣言。
LIST *ls_ptr;
// グローバル変数追加:リストの先頭アドレス
LIST *ls_start;

// リスト構造の使用準備(更新)
void initList(){
 if(ls_start != NULL) return; //追加:すでにリストが作成されている場合は何もしない!
 ls_ptr = malloc(sizeof(LIST));
 ls_start = ls_ptr; // 追加:リストの先頭ポインタを保存
 ls_ptr->next = NULL;  
 ls_ptr->prev = NULL;  
 ls_ptr->value = 0;  
 ls_ptr->index = 0;  
}

// インデックスの最大値を返す。
int getMaxIndex(){
 LIST *lp = ls_start;
 while(lp->next != NULL){
  lp = lp->next;
 }
 return lp->index + 1;
}

// インデックスからリスト要素のポインタを返す。
// 見つからない場合はNULLが返る。
LIST* getPointerFromIndex(int index){
 LIST *lp = ls_start;
 while((lp != NULL)&&(lp->index != index)){
  lp = lp->next;
 }
 return lp;
}

// 使われていない最小インデックス番号を返す。
// 使われていないインデックスがなければ、最大インデックス+1を返す。
int getFreeIndex(){
 LIST *lp = ls_start;
 int i;
 for(i=0;lp != NULL;i++){
  if(lp->index != i) return i;
  lp = lp->next;
 }
 return i+1;
}

// インデックスから要素を取り出す。
int getItemValue(int index){
 LIST *lp = getPointerFromIndex(index);
 if(lp == NULL) return 0;
 return lp->value;
}

// 指定インデックス番号を削除する。
// index=0(開始ポインタ)を削除しようとした場合は何もしない。
LIST* deleteItem(int index){
 LIST *lp = getPointerFromIndex(index);
 if(lp->prev == NULL) goto EXIT_HANDLER;
 lp->prev->next = lp->next;
 lp->next->prev = lp->prev;
 free(lp);
EXIT_HANDLER:
 return lp->next->prev;
}

// 指定インデックスの次に要素を挿入する。
// (インデックスは使用されていない最小の値とする)
LIST* insertItem(int index,int value){
 LIST *lp = getPointerFromIndex(index);
 LIST *new_ptr = malloc(sizeof(LIST));
 new_ptr->next = lp->next;
 new_ptr->prev = lp;
 new_ptr->index = getFreeIndex();
 new_ptr->value = value;
 lp->next = new_ptr;
 return new_ptr;
}

// リストを一括削除する。
// ※free(NULL)は何もしないことが保証されているよ。
void deleteList(){
 LIST *lp = ls_start;
 while(lp != NULL){
  LIST *next = lp->next;
  free(lp);
  lp = next;
 }
 ls_ptr = NULL;
 ls_start = NULL;
}

// リスト構造の末尾に要素を追加する。
int pushList(int val){
 LIST *prev;
 ls_ptr->next = malloc(sizeof(LIST));
 prev = ls_ptr;
 ls_ptr = ls_ptr->next;
 ls_ptr->next = NULL;
 ls_ptr->prev = prev;
 ls_ptr->index = prev->index + 1;
 ls_ptr->value = val;
 return ls_ptr->index;
}

// リスト構造の末尾から要素を取り出す(取り出された要素は削除される)。
int popList(void){
 int val = ls_ptr->value;
 if(ls_ptr->prev == NULL) goto EXIT_HANDLER;
 ls_ptr = ls_ptr->prev;
 free(ls_ptr->next);
 ls_ptr->next = NULL;
EXIT_HANDLER:
 return val;
}

// 現在のリストインデックスを取得。
int listIndex(){
 return ls_ptr->index;
}

// リスト要素の終端をチェックし、要素がない場合はリスト構造を破棄する。
int checkEndOfList(){
 if(ls_ptr->prev == NULL){
  free(ls_ptr);
  ls_ptr = NULL;
  return 0;
 }
 return 1;
}

// エントリポイント
int main(int argc,char **argv){
 int i,max;
 initList();
 pushList(1);
 pushList(2);
 pushList(5);
 pushList(7);
 pushList(8);
 pushList(9);
 pushList(100);
 max = getMaxIndex();
 for(i=0;i < max;i++){
  printf("要素番号 %d : 値 %d\n",i,getItemValue(i));
 }
 return 0;
}

※追記:かなりバグがあったのでだいぶ修正しましたwww

2010年8月25日水曜日

C言語でリスト構造のおさらい。

リハビリを兼ねてC言語でリスト構造のおさらいをしてみました。
正確にはスタックの動作をするリスト構造というべきでしょうか。
これにリストを先頭からスキャンする処理を追加すると、配列的な使い方もできるようになります。
ちなみに、自作テトリスのリプレイ機能は基本的にこのようなリスト構造を使用しています。

// リスト構造(データコンテナ)をC言語で実装してみる。

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

// リスト要素の構造体定義。
typedef struct s_list{
 int index;
 int value;
 struct s_list *next;
 struct s_list *prev;
}LIST;

// 構造体LIST型のグローバルポインタ変数を宣言。
LIST *ls_ptr;

// リスト構造の使用準備
void initList(){
 ls_ptr = malloc(sizeof(LIST));
 ls_ptr->next = NULL;
 ls_ptr->prev = NULL;
 ls_ptr->value = 0;
 ls_ptr->index = 0;
}

// リスト構造の末尾に要素を追加する。
int pushList(int val){
 LIST *prev;
 ls_ptr->next = malloc(sizeof(LIST));
 prev = ls_ptr;
 ls_ptr = ls_ptr->next;
 ls_ptr->next = NULL;
 ls_ptr->prev = prev;
 ls_ptr->index = prev->index + 1;
 ls_ptr->value = val;
 return ls_ptr->index;
}

// リスト構造の末尾から要素を取り出す(取り出された要素は削除される)。
int popList(void){
 int val = ls_ptr->value;
 if(ls_ptr->prev == NULL) goto EXIT_HANDLER;
 ls_ptr = ls_ptr->prev;
 free(ls_ptr->next);
 ls_ptr->next = NULL;
EXIT_HANDLER:
 return val;
}

// 現在のリストインデックスを取得。
int listIndex(){
 return ls_ptr->index;
}

// リスト要素の終端をチェックし、要素がない場合はリスト構造を破棄する。
int checkEndOfList(){
 if(ls_ptr->prev == NULL){
  free(ls_ptr);
  ls_ptr = NULL;
  return 0;
 }
 return 1;
}

// エントリポイント
int main(int argc,char **argv){
 initList();
 pushList(1);
 pushList(2);
 pushList(5);
 pushList(7);
 pushList(8);
 pushList(9);
 pushList(100);
 while(checkEndOfList()){
  printf("要素番号 %d : 値 %d\n",listIndex(),popList());
 }
 return 0;
}

実行イメージ

2010年8月7日土曜日

CVS構築メモ。

ソース管理ツールCVSの構築手順をメモっておく。
eclipseをCVSクライアントとして使用することを想定しています。

★Windows環境
  • CVSNT-SJIS版を今回は使用。
  • インストーラですべて標準設定でセットアップ。
  • CVS接続用のユーザ(通常ユーザでOK)を作成。
  • CVSのルートパスとなるフォルダを作成。(例:C:¥CVSHOME)
  • 環境変数を設定。[CVSROOT=(CVSルートパス)]
  • 必要であればインストールしたCVSのパスを通しておく。(例:C:¥Program Files¥cvsnt¥)
  • [スタート]→[設定]→[コントロールパネル]→[CVS for NT]を開く。
  • CVSサービス及びロックサービスが動いている場合は止めておく。
  • [リポジトリ]タブを選択し、CVSROOTに設定したパスを登録する。
  • [詳細]タブを選択し、[Unix CVSであるふりをする]にチェックし、他のチェックは外す。
  • [サービスの状態]タブに戻り、サービスを再起動する。
  • コマンドプロンプトを開き、[cvs init]を実行、リポジトリを初期化する。
  • 続けてユーザとパスワードを追加する。[例:cvs passwd -r (CVS用Windowsユーザ) -a (追加するユーザ名)]
  • 必要であれば2401ー2402のTCPポートを開けておく。

★Ubuntu(Linux)環境
  • SynapticでCVSとxinetdをインストール。
  • [/etc/xinetd.d/]フォルダ配下に[cvspserver]ファイルを作成※1
  • CVS用システムユーザを作成し、そのユーザでログイン。(例:sudo adduser cvs)
  • CVSのルートパスとなるフォルダを作成。(例:/home/cvs/cvshome)
  • [cvs -d (CVSルートパス) init]を実行し、リポジトリを初期化する。
  • (CVSルートパス)/CVSROOTに移動し、[htpasswd -c passwd (追加するユーザ名)]を実行し、パスワードを登録する。
  • CVSユーザからログオフする。
※1 /etc/xinetd.d/cvspserverの内容(テキストファイル)
    ------------------------------------------------
    service cvspserver
    {
    disable =no
    port =2401
    socket_type =stream
    protocol =tcp
    wait =no
    user =root
    passenv =PATH
    server =/usr/bin/cvs
    env =HOME=(CVSルートパス)
    server_args =-f --allow-root=(CVSルートパス) pserver
    }
    ------------------------------------------------

以上でサーバ側の設定はOK。だと思う。

2010年8月3日火曜日

Android開発近況。

やればやるほど面白くなってきましたAndroid SDK。

今日はこんなコードを書いてみました。
適当にゴチャッと書いたので汚いのは許してね。

ちなみにこのコードはボタンを押すとテキストボックスに入力された
URLでブラウザを開くだけのものですw

package com.imoimo.intent_test;

import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.widget.EditText;
import android.widget.Button;
import android.text.SpannableStringBuilder;

public class Form1 extends Activity implements OnClickListener{

  EditText et;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    LinearLayout ll = new LinearLayout(this);
    ll.setOrientation(LinearLayout.VERTICAL);
    setContentView(ll);
    et = new EditText(this);
    et.setText("http://imoimo2010.blogspot.com/");
    ll.addView(et);
    Button bt = new Button(this);
    bt.setText("ブラウザ起動");
    bt.setOnClickListener(this);
    ll.addView(bt);
  }

  public void onClick(View v){
    SpannableStringBuilder sb = (SpannableStringBuilder)et.getText();
    openBrowser(sb.toString());
  }

  public void openBrowser(String uri){
    try{
      Intent i = new Intent(Intent.ACTION_VIEW,Uri.parse(uri));
      startActivity(i);
    }catch(Exception e){
      viewDialog("エラー","ブラウザの起動に失敗しました。");
    }
  }

  public void viewDialog(CharSequence title,CharSequence msg){
    AlertDialog.Builder dlg;
    dlg = new AlertDialog.Builder(this);
    dlg.setTitle(title);
    dlg.setMessage(msg);
    dlg.setPositiveButton("閉じる", null);
    dlg.show();
  }
}


2010年7月26日月曜日

デジャヴ?

今日は一日姪の相手してました。4連休だと浮かれていた俺がバカでした。
姪の行動力パネエ!・・・あれ?

2010年7月25日日曜日

むしろ肌寒い件について。

今日は朝は蒸し暑くて午後から冷たい風が吹くというなかなかレアな天気の日だった。
つか今これを書いている現在すでに肌寒いんですが。長袖Tシャツ着ちゃったよ俺。

今日も家業の手伝いをしていました。4連休とかいって浮かれていた俺がバカでした。
インゲンの生命力パネエです。ちょっとは手加減してくれw

2010年7月22日木曜日

2010年7月21日水曜日

お引っ越し。

So-netブログからこっちに引っ越してきました。
今後ともよろしくお願いいたします(´∀`)