はじめまして。今回プログラムに関してどうしてもわからないことがあったため、初めて質問させていただきます。
現在自分が制作しているのは、言語はC#で、一定数の人数の中から、当選回数が同じになるよう調整して任意の数抽選するプログラムです。登録した名前にはカウンタがつけられ、当選するごとにカウントを増やし、少ない人から優先して選ぶものとなっています。以下に自分が制作したコードを記載します。
コード:
using System;
using System.Drawing;
using System.Windows.Forms;
partial class MyButton : Button
{
Chusen[] lot = new Chusen[maxPlayer];
int countToSelect = 0;
decimal nokori;
int max;
int[] winner = new int[maxPlayer];
int lotMax = 0;
Random r = new Random();
void OnClicked(object sender, EventArgs e)
{
nokori = (int)dbtn.btn.spin.Value;
max = (int)nokori;
countToSelect = 0;
// 抽選可能な対象を管理する一次抽選インスタンス初期化
for(int i = 0; i < maxPlayer; i++)
{
lot[i] = new Chusen();
}
for(int i = 0; i < maxPlayer; i++)
{
winner[i] = -1;
}
// 抽選可能な人数残っていたら繰り返す
while(nokori > 0)
{
for(int i = 0; i < maxPlayer; i++)
{
lot[i].reset();
}
lotMax = 0;
// 現在抽選可能人数が0だったら
while(lotMax == 0)
{
// すべてのプレイヤーに対して調査
// フラグがオン、非参加モードでない、カウントが最小の物だったら
for(int i = 0; i < maxPlayer; i++)
{
if(dbtn.btn.p[i].getFlag() && dbtn.btn.p[i].join && dbtn.btn.p[i].selected && (dbtn.btn.p[i].count == countToSelect))
{
int k;
if((k = search_Lot()) != -1)
{
// 一次抽選インスタンスにID登録
dbtn.btn.p[i].selected = false;
lot[k].setId(i);
lotMax++;
}
}
}
// ここまで登録されなかったら、カウントを増やしてもう一回
countToSelect++;
}
// 残り人数よりも一次抽選人数が少なかったら全員強制当選
if(lotMax <= nokori)
{
for(int i = 0; i < lotMax; i++)
{
int k = search_winner();
winner[k] = lot[i].getId();
nokori--;
}
}
// そうでなければランダムに選んで当選させる
else
{
for(int i = 0; i < nokori; i++)
{
// dbtn.btn.p[i].selected
int k = search_winner();
winner[k] = r.Next(lotMax - i);
for(int j = winner[k]; j < lotMax - 1; j++)
{
lot[j].setId(lot[j + 1].getId());
}
lot[lotMax - 1].reset();
lotMax--;
}
nokori = 0;
}
lotMax = 0;
}
// 当選者の表示など
string str = "当選者は";
for(int i = 0; i < maxPlayer; i++)
{
if(winner[i] != -1)
{
dbtn.btn.p[winner[i]].count++;
str = str + dbtn.btn.p[winner[i]].getName() + ", ";
}
}
str = str + "です。";
DialogResult result = MessageBox.Show(str, "当選者確定", MessageBoxButtons.OKCancel);
if (result == DialogResult.OK)
{
Clipboard.SetText(str);
}
else
{
for(int i = 0; i < maxPlayer; i++)
{
if(winner[i] != -1)
{
dbtn.btn.p[winner[i]].count--;
}
}
}
// すべて終わったら全員選ばれてないことにする
for(int i = 0; i < maxPlayer; i++)
{
lot[i].reset();
dbtn.btn.p[i].selected = true;
}
}
public int search_winner()
{
for(int i = 0; i < maxPlayer; i++)
{
if(winner[i] == -1)
{
return i;
}
}
return -1;
}
public int search_Lot()
{
for(int i = 0; i < maxPlayer; i++)
{
if(lot[i].getId() == -1)
{
return i;
}
}
return -1;
}
}
// 一次抽選に当選した人を管理するクラス
class Chusen
{
int id;
public Chusen()
{
id = -1;
}
public void reset()
{
id = -1;
}
public void setId(int a)
{
id = a;
}
public int getId()
{
return id;
}
}
このコードを実行すると、配列の範囲外参照などのランタイムエラーは起こらないのですが、一度の抽選で同じ人が二度以上選ばれてしまうという論理エラーが発生します。例えば、参加者が{ a, b, c, d, e, f, g, h }、抽選人数が4人の時、当選者が{ c, d, d, a }のようになってしまいます。このプログラムは人に対して抽選を行うため、このような複数回当選してしまうことはあり得ないようにしなければいけません。このようなエラーが起こってしまうのはなぜでしょうか。また、このエラーを回避する方法があれば、教えていただけるとありがたいです。
現在の開発環境は、Windows10、Microsoft.NETのFramework64、v4.0.30319です。エディタはTeraPad、コマンドプロンプトからcsc.exeを実行してコンパイルしています。
現在C#は勉強したてで、コードの書き方など分からないことは多いですが、C言語でのプログラミング歴があるのである程度のソースコードなら理解することができます。
長くなってしまいましたが、ご教授いただけると幸いです。どうかよろしくお願いします。