ハイスコアランキングの作り方を教えてください

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
TKSZ

ハイスコアランキングの作り方を教えてください

#1

投稿記事 by TKSZ » 18年前

c言語でシューティングゲームを作っているのですが、
ハイスコアランキングを導入しようと思い、
簡単なサンプルプログラムを作成しました。
しかし、思うように動かず困っています。
どこがおかしいのでしょうか?

具体的におかしい部分は、実行すれば分かるのですが、
SADというスコアネームが連続して表示されてしまうのです。

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

struct ScoreName{ /*スコアネーム構造体*/
char name[3];
int score;
};

int BubleSort(ScoreName x[ ], int n) /*バブルソート*/
{
int i, j;
ScoreName temp;

for (i = 0; i < n - 1; i++) {
for (j = n - 1; j > i; j--) {
if (x[j - 1].score < x[j].score) { /* 前の要素の方が小さかったら */
temp = x[j]; /* 交換する */
x[j] = x[j - 1];
x[j - 1] = temp;
}
}
}
}

int main(void)
{
FILE *fp; /*ファイルポインタ*/
ScoreName test[10];/*={"T.S",0,"A.S",0,"NAS",0,"TOM",0,"BAK",0,
             "GOM",0,"VER",0,"HOK",0,"JAK",0,"BOY",0}; */
int i,j,tmp;
ScoreName point={"SAD",0}; /*新しい点数*/


srand(time(NULL)); /*乱数の初期化*/

for(i=0;i<10;i++)
{
/*strcat(test.name,"T.S");*/
point.score = (rand()%2000)+6000; /*新しい点数に6000以上の点数を与える*/
/*test.score=(rand()%3000)+5000;*/
}

fp=fopen("score.dat","r"); /*ファイルオープン*/
for(i=0;i<10;i++)
{
fscanf(fp,"%s",test.name); /*fscanfでネームに文字列を代入*/
fscanf(fp,"%d",&test.score); /*fscanfでスコアに点数を代入*/
}
fclose(fp); /*ファイルクローズ*/

for(i=0;i<10;i++) /*バブルソート前の構造体配列(スコアネーム)を表示*/
{
printf("%s,%d\n",test.name,test.score);
}

for(i=0;i<9;i++) /*最も肝心な部分(スコアが新しいスコアより大きくて、かつ
         スコアが新しいスコアより小さければ新しいスコアを代入する*/
{
if(point.score>test[i+1].score && point.score<=test.score)
{
for(j=i;j<9;j++)
{
test[j+1].score=test[j].score;
}
test=point;
}
}

BubleSort(test,10); /*バブルソートする*/



printf("ソートしました。\n\n"); /*ソートした旨の文字表示*/

fp=fopen("score.dat","w"); /*ファイルオープン(書き込み)*/
for(i=0;i<10;i++)
{
fprintf(fp,"%s\n",test.name);
fprintf(fp,"%d\n",test.score);
}
fclose(fp);

for(i=0;i<10;i++) /*ソート後の構造体配列の中身を表示する*/
{
printf("%s,%d\n",test[i].name,test[i].score);
}

return 0;
}

parapara

Re:ハイスコアランキングの作り方を教えてください

#2

投稿記事 by parapara » 18年前

バブルソート自体が既に間違ってるように思うのですが・・・
気のせいでしょうか?

miyaza

Re:ハイスコアランキングの作り方を教えてください

#3

投稿記事 by miyaza » 18年前

bcc でコンパイルしたらエラーが11個警告が1個出ました。
エラーは構造体変数の宣言のときにstructがついていないのが原因ですが、
そちらの環境は何ですか?

いきなりscore.datを読み込むところから始まるのですが、こちらには存在しないのでまともに実行できません。
名前とスコアが入っていることは分かりますが・・・・・・
score.datの中身も提示して頂けるとありがたいです。

TKSZ

Re:ハイスコアランキングの作り方を教えてください

#4

投稿記事 by TKSZ » 18年前

paraparaさん
>>バブルソート自体が既に間違ってるように思うのですが・・・
>>気のせいでしょうか?
多分あっていると思います。ネットで検索して拝見したバブルソートのソースを
改良して使っています。

miyazaさん
>>bcc でコンパイルしたらエラーが11個警告が1個出ました。
>>エラーは構造体変数の宣言のときにstructがついていないのが原因ですが、
>>そちらの環境は何ですか?
すいません、私の環境は、WindowsMeでBCCを使っています。
あと、c言語で開発しているといいながらも、ソースファイルの拡張子は
.cppでした。多分それが、エラーの原因だと思います。

>>いきなりscore.datを読み込むところから始まるのですが、こちらには存在しないのでまともに実行できません。
>>名前とスコアが入っていることは分かりますが・・・・・・
>>score.datの中身も提示して頂けるとありがたいです。
score.datを添付しますね。

TKSZ

Re:ハイスコアランキングの作り方を教えてください

#5

投稿記事 by TKSZ » 18年前

zipファイルの中には、
score.dat
修正したScoreAttack.c
cpp_test.bdp
cpp_test.exe
が含まれています。

miyaza

Re:ハイスコアランキングの作り方を教えてください

#6

投稿記事 by miyaza » 18年前

スコアリストの途中に新しいスコアを入れてその分ずらしていく部分がおかしいと思いました。

ScoreName構造体をもう一個用意してなるべくもとを変えないようにつくってみました。
(構造体をもう一個使ってる時点でもとを変えている気がしますが)
以下一部
BubleSort(test,10); /*バブルソートする*/
   memcpy(test2,test,sizeof(ScoreName)*10);

   for(i=0;i<10;i++){ 
        if(point.score>test.score){ 
            for(j=i;j<9;j++) test[j+1]=test2[j]; 
            test=point;
	    break;
        }
    }


ScoreName test2[10]としてももう一個構造体を用意しました。
まず、先にバブルソートします。
それからtestをtest2へコピーします。
if文でtest.scoreよりpoint.scoreの方が大きかったら
そこから一個ずつずらしていきます。
その後testにpointを入れてループを抜けます。

たぶんこれでいけると思いますが・・・・・・
そもそも意図と違ったらすみません。

SADが二回表示されるというのが良く分かりませんでした。

バグ

Re:ハイスコアランキングの作り方を教えてください

#7

投稿記事 by バグ » 18年前

名前が3文字ならば、構造体のchar型配列はもう1つ要素を増やしておいた方がいいのではないでしょうか?

TKSZ

Re:ハイスコアランキングの作り方を教えてください

#8

投稿記事 by TKSZ » 18年前

miyazaさん
ありがとうございます。
これで、上手くいきそうです。
もっと精進します。

バグさん
そうですね。何故か、重なるバグと格闘中に
そうしてしまいました。

とろとろ

Re:ハイスコアランキングの作り方を教えてください

#9

投稿記事 by とろとろ » 18年前

初めまして。
とりあえず、バグを3つほどあげます。
・バグ1
for(j=i;j<9;j++) 
{ 
    test[j+1].score=test[j].score; 
}
において、例えばi=5の場合
test[6].score=test[5].score;
test[7].score=test[6].score;
test[8].score=test[7].score;
test[9].score=test[8].score;
が実行され、結果として
test[9].score=test[8].score=test[7].score=test[6].score=test[5].score;
となり、同じスコアが並んでしまう結果となる。

・バグ2
↑の処理ではスコアネーム構造体の要素、名前とスコアの内、スコアしか処理してません。

・バグ3
/*最も肝心な部分(スコアが新しいスコアより大きくて、かつ
スコアが新しいスコアより小さければ新しいスコアを代入する*/
とあるfor文の中にあるfor(j=i;j<9;j++) は、
たぶん1度だけ実行されることを考えてると思いますが、
その処理が終わった後もfor(i=0;i<9;i++)が実行されるので、
何回も実行される可能性があります。

解決案として3つあげます。

・解決案1
for(j=i;j<9;j++) 
{ 
   test[j+1].score=test[j].score; 
} 
test=point;

for(j=9;j>i;j--) 
{ 
   test[j]=test[j-1]; 
} 
test[j]=point; 
break;

とかえる。

・解決案2
ScoreName test[10]をScoreName test[11]と配列を大きくして、
test[10]にScoreName point={"SAD",9999}; /*新しい点数*/ を入れて
BubleSort(test,11)とする。
for(i=0;i<9;i++) /*最も肝心な部分(スコアが新しいスコアより大きくて、かつ 
         スコアが新しいスコアより小さければ新しいスコアを代入する*/ 
{ 
    if(point.score>test[i+1].score && point.score<=test.score) 
    { 
        for(j=i;j<9;j++) 
        { 
            test[j+1].score=test[j].score; 
        } 
        test=point; 
    } 
}

は削除します。
つまり、スコアネーム構造体に空きを作ってそこに新しいスコアネームをいれて、
バブルソートすればいいのではないでしょうか。

・解決案3
/*最も肝心な部分(スコアが新しいスコアより大きくて、かつ
スコアが新しいスコアより小さければ新しいスコアを代入する*/
という、
既にソートしてある配列に新しく要素をいれてまたソートする処理は、
「挿入ソート」とよばれる処理がよいのではないでしょうか。


・解決案2の補足
例えば、
test[0].score=90;
test[1].score=80;
test[2].score=70;
test[3].score=60;
test[4].score=50;
test[5].score=40;
test[6].score=30;
test[7].score=20;
test[8].score=10;
test[9].score=0;/*名前は省略*/
という所に、新しいスコアであるpoint.score=35をいれるとしよう。
その場合、ScoreName test[10]をScoreName test[11]と配列を大きくして、
test[10]=point;と代入すれば、

test[0].score=90;
test[1].score=80;
test[2].score=70;
test[3].score=60;
test[4].score=50;
test[5].score=40;
test[6].score=30;
test[7].score=20;
test[8].score=10;
test[9].score=0;
test[10].score=35;/*名前は省略*/
となる。
そしてtest[0]~test[10]までをソートすれば、
test[0].score=90;
test[1].score=80;
test[2].score=70;
test[3].score=60;
test[4].score=50;
test[5].score=40;
test[6].score=35;/*新しいスコアであるpoint*/
test[7].score=30;
test[8].score=20;
test[9].score=10;
test[10].score=0;
とハイスコア更新処理が完了する。
ハイスコア更新処理のたびにソートするのは処理がかかりそうですが、
10件ぐらいならたいして時間はかからないでしょう。

閉鎖

“C言語何でも質問掲示板” へ戻る