数独(すうどく)は、3×3のブロックに区切られた 9×9の正方形の枠内に1?9までの数字を入れるパズルです。1~9までの数字を入れていくのですが、同じ数字を縦、横、ブロックのなかに2回以上用いることはできません。名称の由来は「数字は独身に限る」の略らしいです。
ではプログラミングで数独を解いてみることにしましょう。
まずユーザーコントロールで以下のようなものを作成します。9個のTextBoxを貼り付けて1つのブロックを作っています。これを3×3個フォームに貼り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
public partial class Block : UserControl { public Block() { InitializeComponent(); } TextBox GetTextBox(int colum, int row) { if(row == 0) { if(colum == 0) return textBox1; if(colum == 1) return textBox2; if(colum == 2) return textBox3; } if(row == 1) { if(colum == 0) return textBox4; if(colum == 1) return textBox5; if(colum == 2) return textBox6; } if(row == 2) { if(colum == 0) return textBox7; if(colum == 1) return textBox8; if(colum == 2) return textBox9; } return null; } public void SetValue(int colum, int row, int i) { GetTextBox(colum, row).Text = i.ToString(); } public int GetValue(int colum, int row) { string str = GetTextBox(colum, row).Text; if(str == "") return 0; try { return int.Parse(str); } catch { return -1; } } public List<int> GetAllValues() { List<int> vs = new List<int>(); for(int row =0; row < 3; row++) { for(int colum=0; colum < 3; colum++) { vs.Add(GetValue(colum, row)); } } return vs; } } |
これをフォームに貼り付けます。
あとは座標を指定して数字を設定する、設定されている数字を取得する、縦列、横列、同じブロック内にすでに存在する数のリストを取得するメソッドを作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
public partial class Form1 : Form { int GetValue(int colum, int row) { Block block = GetBlock(colum, row); return block.GetValue(colum % 3, row % 3); } void SetValue(int colum, int row, int value) { Block block = GetBlock(colum, row); block.SetValue(colum % 3, row % 3, value); } Block GetBlock(int colum, int row) { if(row == 0 || row == 1 || row == 2) { if(colum == 0 || colum == 1 || colum == 2) return block1; if(colum == 3 || colum == 4 || colum == 5) return block2; if(colum == 6 || colum == 7 || colum == 8) return block3; } if(row == 3 || row == 4 || row == 5) { if(colum == 0 || colum == 1 || colum == 2) return block4; if(colum == 3 || colum == 4 || colum == 5) return block5; if(colum == 6 || colum == 7 || colum == 8) return block6; } if(row == 6 || row == 7 || row == 8) { if(colum == 0 || colum == 1 || colum == 2) return block7; if(colum == 3 || colum == 4 || colum == 5) return block8; if(colum == 6 || colum == 7 || colum == 8) return block9; } return null; } /// <summary> /// 指定した場所と同じ横列(行)に存在する数を取得する /// </summary> List<int> GetRowValues(int row) { List<int> vs = new List<int>(); for(int colum=0; colum<9; colum++) vs.Add(GetValue(colum, row)); return vs; } /// <summary> /// 指定した場所と同じ縦列(列)に存在する数を取得する /// </summary> List<int> GetColumValues(int colum) { List<int> vs = new List<int>(); for(int row = 0; row < 9; row++) vs.Add(GetValue(colum, row)); return vs; } /// <summary> /// 指定した場所と同じブロック内に存在する数を取得する /// </summary> List<int> GetBlockValues(int colum, int row) { return GetBlock(colum, row).GetAllValues(); } } |
縦と横、同じブロックに存在する数字はひとつだけなので、指定した場所の縦列、横列、ブロック内に存在する数以外のものがひとつしかないとその場所の値を確定することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public partial class Form1 : Form { List<int> GetEnableValues(int colum, int row) { List<int> vs1 = GetRowValues(row); List<int> vs2 = GetColumValues(colum); List<int> vs3 = GetBlockValues(colum, row); int[] values = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; return values.Except(vs1).Except(vs2).Except(vs3).ToList(); } private void button1_Click(object sender, EventArgs e) { for(int row = 0; row < 9; row++) { for(int colum = 0; colum < 9; colum++) { if(GetValue(colum, row) != 0) continue; List<int> vs = GetEnableValues(colum, row); if(vs.Count == 1) SetValue(colum, row, vs[0]); } } } } |
これで実際に問題どおりに数字を入力してボタンをクリックすると簡単な問題であれば解くことができます。しかし中級以上の問題は工夫が必要です。今回はこれまで。