質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

3回答

459閲覧

C# 配列とListの使い方の違いを教えてください。

turnberry0712

総合スコア2

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2025/05/28 22:40

編集2025/05/29 05:52

実現したいこと

無限(ある程度大きすぎると困るのでX,Yともに256マスと仮定)にマスがある〇×ゲームの書かれた座標の履歴を
無限の盤面がある3かける3のマルバツゲームを作りたいです。(縦[x]3マス・横[y]3マス・無限にしたい部分[z])

byte型の(Listと配列の違いがわかりません)Listか配列にしてプレイヤーの書いた場所を記録したいです。
byte型の(Listと配列の違いがわかりません)Listか三次元配列にしてプレイヤーの示した場所(1ターンに1度)をプレイヤーがゲームをクリアーできる時まで記録したいです。
”2025/05/29 14:40追記事項”マルバツゲームが終わるまで、盤上の情報を持っておきたいです。その為にZ座標を持たせて三次元で考えております。
1枚目(z=0)何も書かれていない紙を保存して、2枚目(z=1)にどこ(x・y)に何(後でマルバツに対応する数字を入れる)を入れたか記録したいです。

発生している問題・分からないこと

配列は最初から決まっている要素しか持っていませんが、
リストはAdd(T)が使えますが3×3×無限とするため、
配列とリストどちらが今回の内容に沿っていて。どのように設定したらよいのでしょうか。

配列は最初から決まっている要素([3[縦マス],3[横マス],無限にしたい問題の箇所)しか持っていませんが、
リストはAdd(T)が使えますが3×3×無限とするため、
配列とリストどちらが今回の内容に沿っていて。どのようにプログラミングしたらよいのでしょうか。

該当のソースコード

C#

1 using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6using System.Windows.Forms; 7 8namespace Hit_And_Blow 9{ 10 public class VariablStorage 11 { 12~~ 13 /* 14 //解答用を記録する配列 15 public byte[,,] answer; 16 */ 17 18 //解答を記録する配列 19 List<byte> answer = new List<byte> { }; 20~~ 21 22 23 24 //解答用を記録する配列 25 **"public byte[,,] answer;"** 26 27 //解答を記録する配列 28 **"List<byte> answer = new List<byte> { };"** 29 30 31 /* 32 * Array.Resize(ref numbers, numbers.Length + 1); 33 * numbers[numbers.Length - 1] = 7; 34 */ 35 36 //初期化関数 37 public void InitializationMethod() 38 { 39 ~~answer = new byte[3, 3, 4];~~ 40 ~~List<byte> numbers = new List<byte> { };~~ 41 _//ここでListか3次元配列のデータを初期化したい。_ 42 } 43 } 44}

C#

1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Data; 5using System.Drawing; 6using System.Linq; 7using System.Text; 8using System.Threading.Tasks; 9using System.Windows.Forms; 10 11 12namespace Hit_And_Blow 13{ 14 public partial class MainForm : Form 15 { 16 //変数置き場 17 public VariablStorage BoardPlace = new VariablStorage(); 18 19 public MainForm() 20 { 21 InitializeComponent(); 22 23 //VariablStorage初期化メソッド 24 BoardPlace.InitializationMethod(); 25 26 } 27 28 29 //初期化関数 30 public void InitializationMethod() 31 { 32 BoardPlace.InitializationMethod(); 33 } 34 } 35} 36

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

https://yossy.penne.jp/wordpress/2024/02/07/c-arrayresizeguid/
ここを見ましたが、一次元配列ならArray.Resize()なのですが、
使いたい配列が三次元配列です。
ここはリストではなく配列が正解なのでしょうか。
よろしくお願いします。

補足

VisualStudio Community 2022
更新プログアムのバージョン17.14.3

デバイス名 user
プロセッサ 12th Gen Intel(R) Core(TM) i7-12700 2.10 GHz
実装 RAM 32.0 GB (31.7 GB 使用可能)
システムの種類 64 ビット オペレーティング システム、x64 ベース プロセッサ
ペンとタッチ 2 タッチ ポイントでのタッチのサポート

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kikukiku

2025/05/29 00:04 編集

無限と言っても実際にはそんなに多くないと思うため 配列でもリストでもどちらでも良いと思います。 自分だったら下記のようにします。 記述が分かりやすくシンプルなものを選びました。 //履歴格納場所を確保 var rireki = new List<Byte[,]>(); //答えがあった var answer1 = new Byte[2, 2]; answer1[0, 0] = 0; answer1[0, 1] = 0; answer1[1, 0] = 0; answer1[1, 1] = 0; //履歴に追加 rireki.Add(answer1); //答えがあった var answer2 = new Byte[2, 2]; answer2[0, 0] = 1; answer2[0, 1] = 1; answer2[1, 0] = 1; answer2[1, 1] = 1; //履歴に追加 rireki.Add(answer2); //履歴から一番古いものを取り出す var answer3 = rireki[0]; //履歴から一番古いものを削除する rireki.RemoveAt(0);
fana

2025/05/29 01:23

> 無限(ある程度大きすぎると困るのでX,Yともに256マスと仮定)にマスがある という話と > 3×3×無限 なる話の対応関係がわからないのは私だけなんでしょうか.
fana

2025/05/29 01:35 編集

> 使いたい配列が三次元配列です こういうのは愚直に多次元にするのではなく,例えば「盤面」という型を用意してやればすっきりするような気がします. 「盤面の一次元配列」という話で済みますから,混乱(?)具合も低減できるのではないでしょうか. > 配列とリストどちら ご自身にとって実装が楽になる側を選択すれば良いのではないでしょうか. (とりあえず List<> の方が楽ができそうかな,という気がします)
turnberry0712

2025/05/29 06:26

kikukiku様 //履歴格納場所を確保 >> var rireki = new List<Byte[,]>(); これを三次元にすれば、盤面(x,y)と何手(z)進んだか分かるのでしょうか。 fana様 3×3のボードが無限個欲しいです。ただ、できる限りいっぱいという訳で厳密には無限ではないですが、何個必要かわからないので無限と書きました。 >「盤面の一次元配列」という話で済みますから,混乱(?)具合も低減できるのではないでしょうか. 盤面は縦横3マスほしいのですが、それを一次元配列にするにはどうすればいいのでしょうか。考えがうまくまとまりませんでした。
kikukiku

2025/05/29 07:07 編集

>//履歴格納場所を確保 >>> var rireki = new List<Byte[,]>(); >これを三次元にすれば、盤面(x,y)と何手(z)進んだか分かるのでしょうか。 2×2から3×3にするためには、上記の定義はそのままでOKです。 下記のように、答えの定義を3×3にすればOKです。 var answer1 = new Byte[3, 3]; answer1[0, 0] = 0; answer1[0, 1] = 0; answer1[0, 2] = 0; answer1[1, 0] = 0; answer1[1, 1] = 0; answer1[1, 2] = 0; answer1[2, 0] = 0; answer1[2, 1] = 0; answer1[2, 2] = 0;
YAmaGNZ

2025/05/29 07:26 編集

もしかして 1手目に0,0に〇 2手目に0,1に× 3手目に1,1に〇 と進んで2手目に戻り 2手目に1,0に× 3手目に2,0に〇 というふうにしたときに1回目の3手目[1,1]〇までの盤面と3手目[2,0]〇までの盤面も覚えておいて やっぱり3手目[1,1]〇の次の手を打ちたいといった感じで自由に行き来できるようにしたいといった感じなんですかね? そうなるとtree構造のほうがいいような気がします。 [0,0]〇 ├[0,1]× │└[1,1]〇 └[1,0]×  └[2,0]〇 といった感じでしょうか
turnberry0712

2025/05/30 01:56

YAmaGNZ様 ご返答頂きありがとうございます。 1手目は白紙にしております。2手目から指定した場所に打つという事がしたいです。 1手目が白紙なのは1手目に戻ると白紙なので初期化しなくていいのかなと思ったからですが、やはり1手目から打った方が良いのでしょうか。 Tree構造は知らないので、いったん調べさせてください。だいぶ時間がかかると思います。なので、アンサーやアンサーを決める時間も多少関わってくると思います。 kikukiku様(2025/05/29 16:07) var answer1 = new Byte[3, 3]; for(int i=0; i<3; i++){ for(int j=0; j<3; j++){ このfor文で回してもよさそうな感じはありますね。
kikukiku

2025/05/30 05:31

>var answer1 = new Byte[3, 3]; >for(int i=0; i<3; i++){ >for(int j=0; j<3; j++){ >このfor文で回してもよさそうな感じはありますね。 ご自由にして頂いてOKかと思います。 基本的な考え方だけ、提示しています。
guest

回答3

0

ベストアンサー

〇×ゲームは三目並べ(Tic tac toe)のことだと思っていますが、合ってますか?
(以下はそうだとします)

また、どのように履歴を保存したいのでしょうか?

リストはAdd(T)が使えますが3×3×無限とするため、

ということですが、私だったら盤面と履歴を独立させて、履歴のほうは

C#

1public class HistoryItem 2{ 3 public int x { get; set; } // x座標 4 public int y { get; set; } // y座標 5 public byte player { get; set; } // ○or× 6}

といったようなクラスにします。
そうすれば、

C#

1 // 定義 2 List<HistoryItem> history = new List<HistoryItem>(); 3 4 // 追加時 5 history.Add(new HistoryItem() { 6 x = 1, 7 y = 1, 8 player = 0 9 });

とすれば、簡単に追加することができ、三次元配列も用意する必要はありません。
また、盤面全体を保持する必要は無いので、メモリの節約にもなります。


もし、配置した座標だけでなく、盤面(の配列)も保持したいのであれば、「配列のリスト」や「リストのリスト」も可能なので、それを使うといいでしょう。

C#

1 // 定義 2 List<byte[,]> history = new List<byte[,]>(); 3 byte[,] answer = new byte[3, 3]; // 現在の盤面 4 5 // 追加時 6 var addHistoryItem = new byte[3, 3]; 7 Array.Copy(answer, addHistoryItem, answer.Length); // 配列のコピー 8 history.Add(addHistoryItem); 9 10 // 参照時 11 int historyNum = 0; // 参照したい履歴の番号 12 for (int y = 0; y < 3; y++) 13 { 14 for (int x = 0; x < 3; x++) 15 { 16 System.Console.Write(history[historyNum][x, y]); 17 if (x == 2) 18 { 19 System.Console.Write("\n"); 20 } 21 else 22 { 23 System.Console.Write(","); 24 } 25 } 26 }

投稿2025/05/29 00:39

fiveHundred

総合スコア10383

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2025/05/29 01:27

〇×ゲームってパスとかあるんでしたっけ?(無いなら 履歴データにplayerを持たせなくても良いかも)
fiveHundred

2025/05/29 02:02

確かに、通常の○×ゲームにはないはずですので、それでも大丈夫そうですね。 (ただ、盤面がanswerという名前になっているので、通常とは異なるかもしれません)
turnberry0712

2025/05/29 06:29

チェスの駒が過去や別次元に戻っていくゲームがあり、 それの三目並べもできそうだなと思って、無限の履歴が欲しかったのです。 伝わりましたでしょうか。説明が下手で大変申し訳ございません。
fiveHundred

2025/05/29 09:38

私の回答で「無限の履歴」は(メモリ不足などにならない限り)可能ですが、どこが不足ですか?
turnberry0712

2025/05/30 02:04

こちらの理解不足から、昨日は分からなかったのですが今日読んでみたらわかりました。 確かにリストでメモリー不足にならない限り無限の履歴が作れると思います。 大変参考になりました。ありがとうございます。 さっそくコードを読み、理解してから、作業を行おうと思っております。 またの発言になりますが、参考になりました。ありがとうございました。
turnberry0712

2025/05/30 02:05

回答ありがとうございます。 試したところ問題が解決しました! ベストアンサーに選ばせていただきました。
guest

0

とりあえず1つの盤面に関する履歴というのを考えると,
3x3の広さなら最大で9手しか打てないのでしょうから
「何手目に,どこの位置に(〇or×が)打たれたか」という話があればよいのかな,と思います.

  • 「何手目に」を配列のindexで表すなら,履歴は最大サイズが9な配列とかでよい.
  • 「どこの位置に」は,3x3箇所しか候補がないのだから 1~9 の数値とかで表現可能.
  • 「〇or×」については,先手がどっちなのかが決まってれば定まるので,いちいち履歴に記録しなくてよい.

というわけで,「どこの位置に」をbyteで表すとしても9byteくらいの情報で済むんじゃないでしょうか.

で,「無限の盤面」(全く同じ場面が多重に存在するのでないならば,有限かもですが)をどうするのか? については
配列orList というよりも,その「履歴」データの用途に応じてデータ構造を決めるべきではないのかな? という気がします.
単純に1次元方向に並んでいれば済むのであれば,List で済ませておけば良いのではないでしょうか.

投稿2025/05/29 06:45

fana

総合スコア12181

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

turnberry0712

2025/05/30 02:01

回答ありがとうございます。 ベストアンサーは別の方を選ばせていただきましたが、こちらの回答も非常に参考になりました。
guest

0

解決済みとなっていますが

チェスの駒が過去や別次元に戻っていく

という言葉よりtree構造が実現したいことを表現できるのかなと思いました。

C#

1 public class HistoryItem 2 { 3 4 // 一手前の履歴 5 public HistoryItem parent; 6 7 // この手の次の手のリスト 8 public List<HistoryItem> child = new List<HistoryItem>(); 9 10 // この手の情報 11 public int count { get; set; } // 何手目 12 public int x { get; set; } // x座標 13 public int y { get; set; } // y座標 14 public byte player { get; set; } // ○or× 15 16 // 盤面 17 public byte[,] ban; 18 19 public HistoryItem(HistoryItem parent, int x, int y, byte player) 20 { 21 this.parent = parent; 22 this.x = x; 23 this.y = y; 24 this.player = player; 25 26 // parentがnullの場合は前の手がない=最初の手ということで盤面を初期化する 27 if (parent == null) 28 { 29 this.ban = new byte[,] { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; 30 } 31 else 32 { 33 // 盤面を前の手からコピー 34 this.ban = parent.ban.Clone() as byte[,]; 35 } 36 37 // 指定された手を反映 38 this.ban[x, y] = player; 39 40 // 何手目か計算 41 if (parent == null) 42 { 43 count = 1; 44 } 45 else 46 { 47 count = parent.count + 1; 48 } 49 50 // 一手前の履歴にこの手を次の手として記録 51 if (parent != null) this.parent.child.Add(this); 52 53 } 54 55 // このクラスの情報を文字列として返す 56 public override string ToString() 57 { 58 string ret; 59 60 ret = $"{new string(' ', count * 2)}{count}手目 {(player == 1 ? "先手" : "後手")} : ({x},{y})"; 61 ret += ban2string(ban); 62 63 return ret; 64 } 65 66 // 盤面を文字列として表現する 67 private string ban2string(byte[,] ban) 68 { 69 string ret; 70 71 ret = $"\n\t{ban[0, 0]}{ban[1, 0]}{ban[2, 0]}"; 72 ret += $"\n\t{ban[0, 1]}{ban[1, 1]}{ban[2, 1]}"; 73 ret += $"\n\t{ban[0, 2]}{ban[1, 2]}{ban[2, 2]}"; 74 75 return ret; 76 } 77 } 78

といった履歴情報を保持するクラスを作成し

C#

1 const int PLAYER1 = 1; 2 const int PLAYER2 = 2; 3 4 static void Main(string[] args) 5 { 6 7 // 1手目 (1) 8 NowHistory = new HistoryItem(null,1,1, PLAYER1); 9 starthistory = NowHistory; 10 11 // 2手目 (2) 12 NowHistory = new HistoryItem(NowHistory, 0, 0, PLAYER2); 13 14 // 3手目 (3) 15 NowHistory = new HistoryItem(NowHistory, 2, 2, PLAYER1); 16 17 18 // 1手目(1)に戻る 19 // NowHistoryが3手目なのでその親(NowHistory.parent)が2手目、2手目の親が1手目となる 20 NowHistory = NowHistory.parent.parent; 21 22 23 // 2手目を新たに指す (4) 24 NowHistory = new HistoryItem(NowHistory, 2, 0, PLAYER2); 25 26 // 3手目 (5) 27 NowHistory = new HistoryItem(NowHistory, 0, 1, PLAYER1); 28 29 // 4手目 (6) 30 NowHistory = new HistoryItem(NowHistory, 2, 1, PLAYER2); 31 32 // ここから最初の3手目(3)に戻る 33 NowHistory = NowHistory.parent.parent.parent.child[0].child[0]; 34 35 // 4手目 (7) 36 NowHistory = new HistoryItem(NowHistory, 0, 1, PLAYER2); 37 38 39 40 Console.WriteLine("履歴を表示"); 41 PrintHistory(starthistory); 42 43 Console.ReadKey(); 44 } 45 46 static void PrintHistory(HistoryItem his) 47 { 48 Console.WriteLine(his.ToString()); 49 50 foreach(var item in his.child) { 51 PrintHistory(item); 52 } 53 } 54

このように手を進めたり戻したりしたいのかなって想像しました。

動作結果としては以下のようになります。

1手目 先手 : (1,1) 000 010 000 2手目 後手 : (0,0) 200 010 000 3手目 先手 : (2,2) 200 010 001 4手目 後手 : (0,1) 200 210 001 2手目 後手 : (2,0) 002 010 000 3手目 先手 : (0,1) 002 110 000 4手目 後手 : (2,1) 002 112 000

とりあえずの実装なので盤面は3x3固定としていますし、不備や不具合等あるかと思いますが基本的な考え方の参考にはなるのではないでしょうか。

投稿2025/05/30 03:30

編集2025/05/30 05:38
YAmaGNZ

総合スコア10557

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

turnberry0712

2025/05/31 11:35

YAmaGNZ様 ベストアンサー後にも謎を考えていただきありがとうございます。 またすぐに壁にぶつかるを思いますが、その際はYAmaGNZ様のコメントを見てから考える事にします。 本当にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問