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();
  }
}