http://www.nposhifa.net/wp-content/uploads/2015/08/0f0d47b8a2a400cb0074e360e215a0bd.pdf
のようなトーナメント表をデータから表示するプログラムが可能なのかどうかおしえてください。
表の特徴は、シードチームが2回戦から登場、3回戦から登場、などがあります。
チーム数から自動的にトーナメント表を出力するプログラムは見たことがありますが、上記の条件では使用不可能と思われます。
回答方法は、URL、またはサンプルプログラムそのものでお願い致します。
できないことはないですが…期待するようなプログラムにはならないと思います。(例はPHPっぽく)
$seed1 = 4;//第一シード高の数 $seed2 = 2;//第二シード高の数 $name = array("1高","2高","3高","4高" …);//高の名前 $seed1name = array("シード1高","強い高","特別高","常勝高");//第一シード高の名前 $seed2name = array("シード2高","主催高");//第二シード高の数
シード高が決まった高校ではない完全ランダムの場合は上、決まっている場合はしたのような処理も必要になります。
あとは、シードの数を考えてトーナメントを表示するプログラムを書けばいいとおもいます。
$team = 18;//全校数 $setfirst = ($set - $seed1 - $seed2)/$seed1 ;//シードを除いた数 割る シード1(小数切捨てしてその分はどこかに付け加える処理をつけないといけない) $result = array(); for($i=0;$i<$seed1;$i++){//シード1は必ずバラけるのでその分会場を増やす $result[$i] = array(); for($k=0;$k<$setfirst;$k++){//シードなし高の数だけまわす $rnd = rnd(0,$team-1);//ランダム数値発生 $result[$i][] = array($name[$rnd],3);//高校名+シード順位 delete $name[$rnd]//使った名前を配列から削除 } //最後にシード高を追加 $rnd = rnd(0,$seed1-1);//ランダム数値発生 $result[$i][] = array($seed1name[$rnd],1); delete $name[$rnd]//使った名前を配列から削除 }
(このコードはdeleteが命令違うので動きませんがこんな感じ)
シード2が入ってませんが、シード1と同じように入れればいけると思います。
あとは作ったデータを使って、線を書いたりするコードを書けばOKかと。
ただ複雑なシードトーナメント表になるとプログラムの作りこみが大変だと思うので、自分で作ったほうが手っ取り早そうな気がします。
トーナメント表は二進木によく似ていますので二進木に表現する方法を考えます。
二進木なら括弧を使うのが一般的だと思いますが一般の人には解りにくいと思うので
回戦とチーム名の表から二進木を表現する方法を考えます。
言語の指定がなかったのでrubyを使いました。
表の表示はこれも指定がなかったのでHTML5のCANVASを使います。確認はDebianのiceweaselで行ないました。
IEではscriptの違いなどで表示できないかもしれません。
データとして
3,藤枝順心高校 1,清水FC女子 1,富士見FCガイア 1,静岡レディース 1,蒲原クラブ女子
2,桐陽高校 1,島田プリンセス 1,アスルクラロ沼津 3,磐田東高校
3,常葉橘高校 1,浜松泉FC 1,静岡大成高校 2,常葉橘中学校
1,東海大静岡翔洋高校 1,御殿場レディース 1,磐田北高校 1,ルクレMYFC 3,清水第八プレアデス
class Node @@org = Struct.new(:x, :y).new(0,0) def initialize(l, r=0, x=nil, y=nil) @label = l @round = r @childs = x ? [x, y] : [] if x lcp = x.cp rcp = y.cp @bounds = lcp+[ 160+48*r,lcp[1], 160+48*r,rcp[1], ]+rcp else @bounds = [ 0,@@org.y, 160,@@org.y, 160,@@org.y+64, 0,@@org.y+64, 0,@@org.y ] @@org.y = @@org.y+64 end # print "#{@label}(#{x.label if x}, #{y.label if y}) #{@bounds.join(',')}\n" end attr_accessor :label, :round def cp() [@bounds[2], (@bounds[3]+@bounds[5])/2] end def find(label) # Enumerableのfindはブロックが値を返すとその値ではなく要素を返すので再帰では意味をなさない。 @label == label ? self : @childs.inject(nil) {|f, c| f ? f : c.find(label) } end def flatten @childs.map {|c| c.flatten }.flatten+ [([@label,@round]+@bounds).join(',')] # ["#{@label},#{@round},#{@bounds.join(',')}"] end end
クラスNodeが二進木の要素です。座標を覚えておく必要があるので多少ごちゃごちゃしています。16を基調とした定数を使っているので状況に合わせて変更する必要があります。
@boundsは表示に必要な座標値で都合があってパスで表しています。ので四角は5ポイントになります。
cp()はチーム名ボックスの右中央の座標です。
find()は二進木の中からlabelのNodeを探すものです。
flatten()は各Nodeの内容を文字にして出力し下記のようになります。
藤枝順心高校,3,0,0,160,0,160,64,0,64,0,0 清水FC女子,1,0,64,160,64,160,128,0,128,0,64 富士見FCガイア,1,0,128,160,128,160,192,0,192,0,128 M1,2,160,96,256,96,256,160,160,160 静岡レディース,1,0,192,160,192,160,256,0,256,0,192 蒲原クラブ女子,1,0,256,160,256,160,320,0,320,0,256 M2,2,160,224,256,224,256,288,160,288 M7,3,256,128,304,128,304,256,256,256 M11,4,160,32,352,32,352,192,304,192 桐陽高校,2,0,320,160,320,160,384,0,384,0,320 島田プリンセス,1,0,384,160,384,160,448,0,448,0,384 アスルクラロ沼津,1,0,448,160,448,160,512,0,512,0,448 M3,2,160,416,256,416,256,480,160,480 M8,3,160,352,304,352,304,448,256,448 磐田東高校,3,0,512,160,512,160,576,0,576,0,512 M12,4,304,400,352,400,352,544,160,544 M15,5,352,112,400,112,400,472,352,472
def parse(t) agenda = t.split("\n").select {|s| s != ''}.map {|line| r, l = line.chomp.split(',') Node.new(l, r.to_i) } gcnt = Math.log2(agenda.inject(0) {|r, n| r + 2**(n.round-1) }) m = 0 1.upto(gcnt) {|r| agenda.each_with_index {|n, i| if n.round == r then # nのroundがrならば次も同じrでなければならない。 y = agenda.delete_at(i+1) agenda[i] = Node.new("M#{m=m+1}", r+1, n, y) end } } agenda[0] end
parseはトーナメントのデータから二進木ほ生成する部分です。
まずチームごとにNodeを生成してagendaへ追加していきます。
Math.log2(agenda.inject(0) {|r, n| r + 2**(n.round-1) })
は何回戦必要か計算します。
次にagendaから1回戦ペアーを取り出し2回戦として登録していきます。
同じくagendaから2回戦ペアーを取り出し3回戦として登録していくことを必要な回戦分繰り返すことで二進木として全体が登録されます。
エラーチェックはしていないのでdataが変だと止まります。
def ptpath(path, rvs) path.each_slice(2).map {|p| rvs ? [ rvs[0]-p[0].to_i, p[1].to_i-rvs[1] ] : [ p[0].to_i, p[1].to_i ] } end def canvas_to_html(games, rvs=nil) cout = ['context.font = "16px sans-serif";'] puts games.join("\n") puts "\n" games.each {|game| label, round, *path = game.split(',') path = ptpath(path, rvs) x, y = path.shift cout << "context.beginPath();context.moveTo(#{x},#{y});" path.each {|x, y| cout << "context.lineTo(#{x},#{y});" } cout << "context.stroke();" if path.size == 3 x, y = path.shift x = rvs ? x-8-label.length*8 : x+8 y = (y+path[0][1])/2-4 else if rvs x = x-160/2-label.length*16/2 y = y+32 else x = x+160/2-label.length*16/2 y = y+32 end end cout << "context.fillText(\"#{label}\",#{x},#{y});" } cout.join("\n") end
ptpathは座標値データで文字で入っているのでそれを数値に直すものです。
canvas_to_htmlはgamesにNodeの情報が文字列の配列で格納されているのでそれを展開しながらcanvas用のscriptに変更します。
反転も考慮しているので煩雑になっています。
root = parse ARGF.readlines.join canvas_script = canvas_to_html(root.find('M15').flatten)+ canvas_to_html(root.find('M16').flatten, [900,576]) template = <<"html" <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>トーナメント表</title> <script type="text/javascript"> <!-- function draw(context) { #{canvas_script} } function drawcanvas(name) { //描画コンテキストの取得 var canvas = document.getElementById(name); if (canvas.getContext) draw(canvas.getContext('2d')); } //--> </script> </head> <body onLoad="drawcanvas('canvas0')"> <h2>トーナメント表</h2> <h3 style="text-align: center;">平成27年度 第37回 全日本女子サッカー選手権 静岡県決勝大会<br>組 合 せ</h3> <canvas id="canvas0" width="1024" height="1280" style="background-color:pink;"> 図形を表示するには、canvasタグをサポートしたブラウザが必要です。 </canvas> </body> </html> html open("index.html", "w") {|f| f.write(template) }
root = parse ARGF.readlines.join
メインの流れはARGF.readlines.joinでデータを取り込みそれをparseします。二進木はrootに格納されます。
canvas_script = canvas_to_html(root.find('M15').flatten)+
canvas_to_html(root.find('M16').flatten, [900,576])
canvas_to_html(root.find('M15').flatten)はM15からのトーナメント表のcanvas用scriptを出力します。
canvas_to_html(root.find('M16').flatten, [900,576])はM16からのトーナメント表を反転移動したcanvas用scriptを出力します。
canvas_script = canvas_to_html(root.flatten)とすることで全体のトーナメント表を表示させるとこも可能です。
ヒアドキュメントによってcanvas_scriptが埋め込まれたhtmlがtemplateに格納され
open("index.html", "w") {|f| f.write(template) }
でindex.htmlファイルに出力されます。
動作確認用ですのでコードは汚いです。結果も反映させたいのであれば入力データはカンマで区切られたCSVファイルのようなものなので情報の追加してNodeに反映させることで可能になるかと。
pdfにあるような綺麗なトーナメント表にするには条件の設定などが面倒になりあまり実用的ではないように感じました。
組み合わせ決定後や試合結果を表にしたいのですね。
2015/08/19 10:36:11どうでしょうね…勝ち負けなどのデータをプログラムで表記していくのはさらに難しそうですね…
配列("優勝高","シード1","第一試合結果5-3","4-2","決勝戦3-0")
配列("1高","シード3","第一試合結果3-5")
とかデータ形式を決めて、配列に入っているデータによって表示を変える形になるのかな。
校名 第1回戦 第2回戦 第3回戦 第4回戦
2015/08/19 20:45:49A校 * b c d
B校 a b c d
C校 * * * d
D校 a b c d
E校 * * c d
こんな感じの表はできたのですが…