CODE VS 2.1 をSQLでやるとどうなる6
前回の続きです。今回は消すブロックを選択するところまで作ります。
作るのは http://codevswc.jp/jpn/rule.html これです。
是非、ルールをよく読んで、「自分なら SQL でこういう方針で作る」
「Javaなら、.Netなら、Rubyなら…… こういう方針で作る」
と想像しながら読んでください。
多分、多くの人が、「『消すブロックを選択するところ』が一番、SQLでできない」と考えるところじゃないかと思っています。が、この辺の処理は極めてSQL的です。
最初の方で作った4方向、8方向へオフセットするテーブルを使いますから、勘のいい人はアレを観た時点で分かったと思いますが……。
現状のデータでは分かりにくいので新たなデータでやります
このSQL http://www.g1sys.co.jp/TestB.txt を流して確認すると、
-- 確認 SELECT * FROM DispBoard10(-1, 0, 0, 0) ORDER BY Row DESC;
図の様に返ってきます。
以降、これをベースに考えます。
色が変わっているブロックを選択出来れば成功です。
4方向か8方向か
まずは消すブロックをカウントするのですけれど、今、落としたブロックは分かるわけですから、落としたブロックだけを8方向に合算して行き、合計が条件と一致する(現在のゲームでは10)になれば消すブロックと考えれば良いことです。
(1, 0)の位置にある[5]から右下方向に合算して行って(0, 1)の[5]を見つけ、「消すブロック」と決定することができます。
さらに、(0, 0)の位置の[1]、(1, 1)の位置の[11]はどの方向合算していっても消せませんが、(0, 1)の[5]を左上に合算をして行ったとき、既に選択済みの(1, 0)の[5]が二重に選択されます。単純な個数なら重複して抽出し、後から重複を消せばすむ話ですが、ルールからオレンジの(0, 1)の[5]は二重計上しなければ行けません。
この複雑さを回避するために、全部の数字を4方向にチェックしていくのが一般的と思います。(ちなみに8方向に合算していくと、必ず二重計上され無駄です)
いくつかフラグを足せばSQLでも、落としたブロックだけをチェックするだけでできなくはないのですが、解説が大変なので4方向に合算していくやり方にしました(苦笑)
恐らく、パフォーマンス的にはそんなに変わらないと思います。
再帰SQLとCROSS JOIN
WITH cb (GameID, SimulationID, Turn, Row, Col , ERow, ECol, Num, Amount, Cnt, Angle, Memo) -- 4方向に合算された結果 AS( SELECT GameID , SimulationID , Turn , Row , Col , Row AS ERow , Col AS ECol , Num , Num AS amount , 1 AS cnt , 0 --調べている位置 , CAST(N'調査' AS nvarchar(8)) AS Memo FROM Board WHERE GameID =-1 AND Turn = 0 AND Num < 10 UNION ALL SELECT cb.GameID , cb.SimulationID , cb.Turn , cb.Row -- 調査元の行 , cb.Col -- 調査元の列 , bd.Row -- 調査先の行 , bd.Col -- 調査先の列 , bd.Num , cb.Amount + bd.Num , Cnt + 1 , dr.Angle , dr.Memo FROM Board AS bd INNER JOIN (cb CROSS JOIN Direction4 AS dr) ON bd.Row = cb.Row + (dr.RowOffset * Cnt) AND bd.Col = cb.Col + (dr.ColOffset * Cnt) AND bd.GameID = cb.GameID AND bd.SimulationID = cb.SimulationID AND bd.turn = cb.turn WHERE cb.amount + bd.num <= 10 ) -- SELECT * FROM cb; -- として、以下をコメントアウトすれば4方向に合算した結果が確認できる , eb (GameID, SimulationID, Turn, Row, Col , ERow, ECol, Num, Amount, Cnt, Angle, Memo) -- 消すブロック AS( SELECT * FROM cb AS bd WHERE EXISTS ( SELECT * FROM cb WHERE bd.row = cb.row AND bd.col = cb.col AND (bd.angle = 0 OR bd.angle = cb.angle) AND cb.amount = 10 ) ) -- WITHの終わり -- SELECT * FROM eb; -- でより詳しい情報が表示されます。 SELECT ERow, ECol, Num FROM eb;
実行すれば、(0, 1)の[5]が重複して、都合5つのブロックが選択されるはずです。
つづく