PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/org/mahjong/matoso/service/RoundService.java

http://matoso.googlecode.com/
Java | 356 lines | 223 code | 37 blank | 96 comment | 65 complexity | e3cbb20fe81c0cf9ca1d56b3eadca1b3 MD5 | raw file
  1. /* MATOSO project - 2009
  2. *
  3. * This acronym stands for MAhjong TOurnament SOftware.
  4. * Originally created by Nicolas Pochic and Clement Trung.
  5. * Feel free to modify and redistribute this code wherever you want.
  6. * Software is distributed on an "AS IS" BASIS,
  7. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  8. */
  9. package org.mahjong.matoso.service;
  10. import java.sql.Date;
  11. import java.sql.Time;
  12. import java.util.ArrayList;
  13. import java.util.LinkedHashSet;
  14. import java.util.List;
  15. import java.util.Set;
  16. import org.apache.log4j.Logger;
  17. import org.hibernate.Query;
  18. import org.hibernate.Session;
  19. import org.mahjong.matoso.bean.Player;
  20. import org.mahjong.matoso.bean.Round;
  21. import org.mahjong.matoso.bean.Table;
  22. import org.mahjong.matoso.bean.Team;
  23. import org.mahjong.matoso.bean.Tournament;
  24. import org.mahjong.matoso.util.HibernateUtil;
  25. import org.mahjong.matoso.util.exception.FatalException;
  26. /**
  27. * Round layer.<br>
  28. * Methods to fill tables too.
  29. *
  30. * @author npochic
  31. * @date 14 mars 2010
  32. */
  33. public abstract class RoundService {
  34. private static final Logger LOG = Logger.getLogger(RoundService.class);
  35. /**
  36. * Get the rounds up and running by filling all the tables. <br>
  37. * The algorithm is a brutal force calculating one.
  38. *
  39. * @param players
  40. * the list of players
  41. * @param teams
  42. * the list of teams, null if no team
  43. * @param nbTries
  44. * number of tries (a high number could slow the execution)
  45. * @param maxTimeToWait
  46. * the number of seconds to wait if the number of tries is too
  47. * high
  48. * @param nbMaxRounds
  49. * the max. rounds
  50. *
  51. * @return the list tables for the specified rounds
  52. */
  53. public static List<Table> fillTables(Set<Player> players, Set<Team> teams, int nbTries, int maxTimeToWait, int nbMaxRounds) {
  54. LOG.debug("nbTries=" + nbTries);
  55. long t0 = System.currentTimeMillis();
  56. List<Table> max = new ArrayList<Table>();
  57. List<Table> current = new ArrayList<Table>();
  58. while (nbTries-- > 1 && System.currentTimeMillis() - t0 < maxTimeToWait * 1000) {
  59. fillTables(current, players, teams, -1, nbMaxRounds);
  60. if (current.size() * 4 / players.size() >= nbMaxRounds) {
  61. max.clear();
  62. if (current.size() * 4 / players.size() > nbMaxRounds) {
  63. // Delete some tables
  64. for (Table tableI : current)
  65. if (tableI.getRoundNbr() <= nbMaxRounds)
  66. max.add(tableI);
  67. } else
  68. max.addAll(current);
  69. break;
  70. }
  71. if (current.size() > max.size()) {
  72. max.clear();
  73. max.addAll(current);
  74. }
  75. if (nbTries % 10000 == 0) {
  76. LOG.debug("time=" + ((System.currentTimeMillis() - t0) / 1000) + "\tnbTry=" + nbTries + "\tmax=" + max.size() * 4 / players.size());
  77. }
  78. current.clear();
  79. }
  80. LOG.debug("FINAL time=" + ((System.currentTimeMillis() - t0) / 1000));
  81. LOG.debug("FINAL nbTry=" + nbTries);
  82. LOG.debug("FINAL nbrounds=" + max.size() * 4 / players.size());
  83. return max;
  84. }
  85. public static int fillTables(List<Table> tables, Set<Player> playersSet, Set<Team> teams, int addMoreRounds, int nbRoundMax) {
  86. Player[] players = playersSet.toArray(new Player[] {});
  87. int cptRepeat = 0;
  88. Table table;
  89. int numberTables = players.length / 4;
  90. int indexPlayer, cptLoop, roundNumber = 0;
  91. List<Player> playersToAdd = new ArrayList<Player>();
  92. Player player = null;
  93. do {
  94. roundNumber++;
  95. if (roundNumber > nbRoundMax)
  96. break;
  97. // Get a list of random players
  98. playersToAdd.clear();
  99. while (playersToAdd.size() != players.length) {
  100. indexPlayer = (int) Math.floor(Math.random() * players.length);
  101. if (!playersToAdd.contains(players[indexPlayer]))
  102. playersToAdd.add(players[indexPlayer]);
  103. }
  104. for (int countTable = 1; countTable <= numberTables; countTable++) {
  105. // Add a new table
  106. table = new Table();
  107. table.setRoundNbr(roundNumber);
  108. table.setName(Integer.toString(countTable));
  109. // Find 4 players
  110. indexPlayer = 0;
  111. cptLoop = 0;
  112. while (table.getListPlayers().size() != 4) {
  113. if (indexPlayer == playersToAdd.size()) {
  114. indexPlayer = 0;
  115. cptLoop++;
  116. if (cptLoop == 2) {
  117. cptLoop = 0;
  118. if (addMoreRounds == -1)
  119. break;
  120. else if (playersToAdd.contains(player)) {
  121. cptRepeat++;
  122. if (LOG.isDebugEnabled())
  123. LOG.debug(roundNumber + " > " + table + " > " + player);
  124. // Add the previous player to the table
  125. TableService.addPlayerToTable(player, table);
  126. playersToAdd.remove(player);
  127. // Restart the loop
  128. continue;
  129. }
  130. }
  131. }
  132. player = playersToAdd.get(indexPlayer);
  133. // Check the history of the current player
  134. if ((teams == null || !checkTeam(teams, table, player)) && !checkAlreadyPlayed(tables, table, player)) {
  135. // Add the player to the table
  136. TableService.addPlayerToTable(player, table);
  137. playersToAdd.remove(player);
  138. continue;
  139. }
  140. indexPlayer++;
  141. }
  142. if (table.getListPlayers().size() == 4) {
  143. tables.add(table);
  144. } else {
  145. break;
  146. }
  147. }
  148. } while (tables.size() == numberTables * roundNumber && (addMoreRounds == -1 || --addMoreRounds > 0));
  149. Set<Table> lastRound = new LinkedHashSet<Table>();
  150. for (Table tableI : tables) {
  151. if (tableI.getRoundNbr() == roundNumber) {
  152. lastRound.add(tableI);
  153. }
  154. }
  155. if (lastRound.size() != numberTables) {
  156. for (Table tableI : lastRound) {
  157. tables.remove(tableI);
  158. }
  159. }
  160. return cptRepeat;
  161. }
  162. /**
  163. * Test if a player is playing with another player of his team at the table.
  164. *
  165. * @param listTeams
  166. * the list of all the teams
  167. * @param table
  168. * @param player
  169. *
  170. * @return true if another player of the team is at this table, false
  171. * otherwise.
  172. */
  173. private static boolean checkTeam(Set<Team> listTeams, Table table, Player player) {
  174. Team team = TeamService.getTeamForPlayer(listTeams, player);
  175. if (team != null)
  176. for (Player playerTeam : table.getListPlayers()) {
  177. if (team.getPlayers().contains(playerTeam))
  178. return true;
  179. }
  180. return false;
  181. }
  182. /**
  183. * Test if a player has already played with one of the player of this table.
  184. *
  185. * @param listTables
  186. * the list of all the tables filled until now.
  187. * @param table
  188. * @param player
  189. *
  190. * @return true if the player has already played with one of the player of
  191. * this table, false otherwise.
  192. */
  193. private static boolean checkAlreadyPlayed(List<Table> listTables, Table table, Player player) {
  194. for (Table tableI : listTables)
  195. for (Player playerTable : table.getListPlayers())
  196. if (tableI.getListPlayers().contains(playerTable) && tableI.getListPlayers().contains(player))
  197. return true;
  198. return false;
  199. }
  200. public static void addMoreRounds(List<Table> currentTables, Set<Player> players, Set<Team> teams, int nbRounds, int nbTries, int maxTimeToWait) {
  201. LOG.debug("nbTries=" + nbTries);
  202. long t0 = System.currentTimeMillis();
  203. Set<Table> best = new LinkedHashSet<Table>();
  204. int minRepeat = Integer.MAX_VALUE, currentRepeat;
  205. while (nbTries-- > 1 && System.currentTimeMillis() - t0 < maxTimeToWait * 1000) {
  206. currentRepeat = fillTables(currentTables, players, teams, nbRounds, 1000);
  207. if (currentRepeat < minRepeat) {
  208. minRepeat = currentRepeat;
  209. best.clear();
  210. best.addAll(currentTables);
  211. }
  212. if (nbTries % 100 == 0) {
  213. LOG.debug("time=" + ((System.currentTimeMillis() - t0) / 1000) + "\tnbTry=" + nbTries + "\tminRepeat=" + minRepeat);
  214. }
  215. // Remove the added rounds
  216. for (int i = 0; i < nbRounds; i++)
  217. currentTables.remove(currentTables.size() - 1);
  218. }
  219. currentTables.clear();
  220. currentTables.addAll(best);
  221. LOG.debug("FINAL time=" + ((System.currentTimeMillis() - t0) / 1000));
  222. LOG.debug("FINAL nbTry=" + nbTries);
  223. LOG.debug("FINAL minRepeat=" + minRepeat);
  224. LOG.debug("FINAL nbrounds=" + best.size() * 4 / players.size());
  225. }
  226. /**
  227. * Get all rounds of the tournament ordered by number with tables inside.
  228. *
  229. * @param tournament
  230. * @return
  231. * @throws FatalException
  232. */
  233. @SuppressWarnings("unchecked")
  234. public static List<Round> getRounds(Tournament tournament) throws FatalException {
  235. Session session = HibernateUtil.getSession();
  236. Query query = session.createQuery("from Round where tournament = :t order by number");
  237. query.setParameter("t", tournament);
  238. return (List<Round>) query.list();
  239. }
  240. /**
  241. * Save a round.
  242. *
  243. * @param tournament
  244. * @param number
  245. * @param tables
  246. * @throws FatalException
  247. */
  248. public static void create(Tournament tournament, Integer number, List<Table> tables) throws FatalException {
  249. Round round = new Round();
  250. round.setNumber(number);
  251. round.setTables(tables);
  252. round.setTournament(tournament);
  253. HibernateUtil.save(round);
  254. }
  255. /**
  256. * Get a round by its id.
  257. *
  258. * @param id
  259. * @return
  260. * @throws FatalException
  261. */
  262. public static Round getById(Integer id) throws FatalException {
  263. return (Round) HibernateUtil.getSession().load(Round.class, id);
  264. }
  265. /**
  266. * Update a round.
  267. *
  268. * @param id
  269. * @param date
  270. * @param startTime
  271. * @param finishTime
  272. *
  273. * @throws FatalException
  274. */
  275. public static void update(Integer id, Date date, Time startTime, Time finishTime) throws FatalException {
  276. Round round = getById(id);
  277. round.setDate(date);
  278. round.setStartTime(startTime);
  279. round.setFinishTime(finishTime);
  280. HibernateUtil.save(round);
  281. }
  282. public static boolean isFilledWithTotal(int id) {
  283. try {
  284. Round round = getById(id);
  285. for (Table table : round.getTables())
  286. if (GameResultService.isEmpty(table.getResult()))
  287. return false;
  288. } catch (FatalException e) {
  289. LOG.error("error to find the round " + id, e);
  290. }
  291. return true;
  292. }
  293. public static boolean isFilledWithGames(int id) {
  294. try {
  295. Round round = getById(id);
  296. for (Table table : round.getTables())
  297. if (table.getGames() == null || table.getGames().isEmpty())
  298. return false;
  299. } catch (FatalException e) {
  300. LOG.error("error to find the round " + id, e);
  301. }
  302. return true;
  303. }
  304. public static int getLastPlayedSession(Tournament tournament) {
  305. int i = 0;
  306. try {
  307. List<Round> list = getRounds(tournament);
  308. for (Round round : list) {
  309. if (isFilledWithTotal(round.getId()))
  310. i++;
  311. else
  312. break;
  313. }
  314. } catch (FatalException e) {
  315. LOG.error("error to get rounds for tournament " + tournament.getName() + " (" + tournament.getId() + ')', e);
  316. }
  317. return i;
  318. }
  319. }