1 module mjtournament.tournament; 2 3 enum size_t playerPerTable = 4; 4 import std.algorithm, std.range, std.array, std.string, std.format, std.conv, std.random; 5 6 struct Tournament 7 { 8 immutable size_t tables, players, rounds; 9 size_t[][] assignment; 10 pure this (in size_t tables, in size_t players, in size_t rounds) 11 { 12 this.tables = tables; 13 this.players = players; 14 this.rounds = rounds; 15 this.assignment = new size_t[][] (players, rounds); 16 } 17 size_t[][] incidenceMatrix() 18 { 19 auto ret = new size_t[][] (players, players); 20 foreach (i, p; assignment) 21 foreach (j, q; assignment) 22 if (i == j) 23 break; 24 else 25 foreach (r; 0..rounds) 26 ret[j][i] = ret[i][j] += p[r] == q[r] && p[r] < tables; 27 return ret; 28 } 29 void toString(scope void delegate(const(char)[]) sink, 30 FormatSpec!char fs) const 31 { 32 switch (fs.spec) 33 { 34 case 's', 'p': // default (player-centered) format 35 foreach (player; assignment) 36 sink("%(%d %)\n".format(player)); 37 break; 38 case 't': // table-centered format 39 foreach (round; 0..rounds) 40 { 41 foreach (table; 0..tables) 42 sink("%(%d %)\n".format(players.iota.filter!(player => assignment[player][round] == table))); 43 if (round + 1 < rounds) 44 sink("\n"); 45 } 46 break; 47 case 'l': // one-liner format 48 sink("%d %d %d".format(tables, players, rounds)); 49 foreach (round; 0..rounds) 50 foreach (player; 0..players) 51 sink(" %d".format(assignment[player][round])); 52 break; 53 default: 54 throw new Exception("Unknown format specifier: %" ~ fs.spec); 55 } 56 } 57 } 58 59 pure Tournament skipRounds(Tournament tournament, size_t skip) 60 in 61 { 62 assert (skip <= tournament.rounds); 63 } 64 body 65 { 66 auto ret = Tournament(tournament.tables, tournament.players, tournament.rounds - skip); 67 ret.assignment = tournament.assignment.map!(a => a[skip..$]).array; 68 return ret; 69 } 70 71 72 Tournament scramble(in Tournament tournament, in size_t teamSize) 73 { 74 assert (tournament.players % teamSize == 0); 75 auto teams = tournament.players / teamSize; 76 auto ret = Tournament(tournament.tables, tournament.players, tournament.rounds); 77 size_t p; 78 foreach (team; teams.iota.randomCover) 79 foreach (idx; teamSize.iota.randomCover) 80 ret.assignment[p++] = tournament.assignment[team * teamSize + idx].dup; 81 return ret; 82 } 83 84 Tournament scrambleTables(Tournament tournament) 85 { 86 auto ret = Tournament(tournament.tables, tournament.players, tournament.rounds); 87 assert (false); 88 } 89 90 /** Read a tournament from one-liner string. 91 92 Input line must be a sequence of non-negative integers separated by white spaces, which start with numbers of tables, players, and rounds. 93 The following (players * rounds) numbers are interpreted as the tables at which players play in the rounds of the tournament. 94 95 example: 96 toTournament("1 5 5 [1 0 0 0 0] [0 1 0 0 0] [0 0 1 0 0] [0 0 0 1 0] [0 0 0 0 1]") (brackets for explanation) 97 is a trivial tournament played by one table with 5 players. 98 Each section in the brackets is a round, each element of which is the number of the table each player plays at. 99 100 */ 101 pure Tournament toTournament(string x) 102 { 103 auto buf = x.strip.split(",")[0].split.map!(to!size_t); 104 auto tournament = Tournament(buf[0], buf[1], buf[2]); 105 buf = buf[3..$]; 106 foreach (round; 0..tournament.rounds) 107 foreach (player; 0..tournament.players) 108 { 109 tournament.assignment[player][round] = buf.front; 110 buf.popFront; 111 } 112 return tournament; 113 } 114 115 /** Read a tournament from a string array. 116 117 Each line corresponds to a player and is indicating the tables at which the player plays in the rounds. 118 */ 119 pure Tournament toTournament(string[] x) 120 { 121 x = x.filter!(e => e.length).array; 122 immutable 123 players = x.length, 124 tables = players / playerPerTable; 125 auto buf = x.map!(e => e.strip.split.map!(to!size_t).array).array; 126 immutable rounds = buf[0].length; 127 auto tournament = Tournament(tables, players, rounds); 128 foreach (player, assignment; buf) 129 foreach (round, table; assignment) 130 tournament.assignment[player][round] = table; 131 return tournament; 132 } 133 134 135 136