simu.c (12865B)
1 /* 2 * Input: 3 * <number of teams> 4 * Team1 5 * Team2 6 * 7 * <number of played games> 8 * <index of home team> <index of away team> <home score> <away score> <OT/SO> 9 * 10 * <number of remaining games> 11 * <index of home team> <index of away team> 12 * 13 * Output: 14 * Team1 <current games> <current points> <elo rating> <total games> <expected points> <probab finish 1st> <probab finish 2nd> ... 15 * Team2 <current games> <current points> <elo rating> <total games> <expected points> <probab finish 1st> <probab finish 2nd> ... 16 */ 17 #define _POSIX_C_SOURCE 200811L 18 #include <limits.h> 19 #include <math.h> 20 #include <pthread.h> 21 #include <stdbool.h> 22 #include <stdint.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #if defined(USE_LEHMER) 29 #define R_MAX UINT32_MAX 30 struct rand_t { 31 __uint128_t state; 32 }; 33 34 static void getseed(struct rand_t *r) 35 { 36 struct timespec s; 37 clock_gettime(CLOCK_MONOTONIC, &s); 38 r->state = s.tv_nsec; 39 r->state <<= 64; 40 r->state |= s.tv_sec | 1; 41 } 42 43 static inline uint32_t getrand(struct rand_t *r) 44 { 45 r->state *= 0xda942042e4dd58b5; 46 return r->state >> 64; 47 } 48 49 #elif defined(USE_URANDOM) 50 #define R_MAX UINT32_MAX 51 struct rand_t { 52 int fd; 53 uint32_t rbuf[4096]; 54 uint32_t *r; 55 }; 56 57 static void getseed(struct rand_t *r) 58 { 59 r->fd = open("/dev/urandom", O_RDONLY); 60 r->r = 1[&r->rbuf]; 61 } 62 63 static inline uint32_t getrand(struct rand_t *r) 64 { 65 if (r->r == 1[&r->rbuf]) { 66 read(r->fd, r->rbuf, sizeof(r->rbuf)); 67 r->r = r->rbuf; 68 } 69 return *r->r++; 70 } 71 #else 72 struct rand_t { 73 unsigned int seed; 74 }; 75 #define R_MAX RAND_MAX 76 static void getseed(struct rand_t *r) 77 { 78 struct timespec s; 79 clock_gettime(CLOCK_MONOTONIC, &s); 80 r->seed = s.tv_sec ^ s.tv_nsec; 81 } 82 83 static inline uint32_t getrand(struct rand_t *r) 84 { 85 return rand_r(&r->seed); 86 } 87 #endif 88 89 static inline bool urand(uint32_t limit, bool *anti, struct rand_t *r) 90 { 91 uint32_t v = getrand(r); 92 if (anti) 93 *anti = (uint32_t)R_MAX - v < limit; 94 return (uint32_t)v < limit; 95 } 96 97 struct team { 98 size_t id; 99 char name[128]; 100 unsigned points; 101 unsigned games; 102 unsigned wins; 103 double elo; 104 long long unsigned pointsum; 105 long long unsigned gamessum; 106 size_t *poscounts; 107 }; 108 109 struct game { 110 size_t t[2]; 111 uint32_t expected; 112 }; 113 114 struct standing { 115 size_t ti; 116 unsigned points; 117 unsigned wins; 118 unsigned games; 119 unsigned random; 120 }; 121 122 int pointscmp(const void *a, const void *b) 123 { 124 int r; 125 const struct standing *at = a; 126 const struct standing *bt = b; 127 128 if ((r = bt->points * at->games - at->points * bt->games)) 129 return r; 130 if ((r = bt->wins - at->wins)) 131 return r; 132 return bt->random - at->random; 133 } 134 135 int teampointscmp(const void *a, const void *b) 136 { 137 int r; 138 const struct team *at = a; 139 const struct team *bt = b; 140 141 if ((r = bt->points * at->games - at->points * bt->games)) 142 return r; 143 if ((r = bt->wins - at->wins)) 144 return r; 145 return 0; 146 } 147 148 int teamcmp(const void *a, const void *b) 149 { 150 const struct team *at = a; 151 const struct team *bt = b; 152 153 if (at->pointsum * bt->gamessum > bt->pointsum * at->gamessum) 154 return -1; 155 if (bt->pointsum * at->gamessum > at->pointsum * bt->gamessum) 156 return 1; 157 return 0; 158 } 159 160 void isort(void *data, size_t n, size_t sz, int (*cmp)(const void *a, const void *b)) 161 { 162 char (*d)[sz][1] = data; 163 char tmp[sz]; 164 size_t i, j; 165 166 for (i = 1; i < n; i++) { 167 if (cmp(d[i], d[i - 1]) >= 0) 168 continue; 169 170 memcpy(tmp, d[i], sizeof(tmp)); 171 172 for (j = i - 1; j > 0 && cmp(tmp, d[j - 1]) < 0; j--); 173 174 memmove(d[j + 1], d[j], *d[i] - *d[j]); 175 memcpy(d[j], tmp, sizeof(tmp)); 176 } 177 } 178 179 void simulate(struct team *teams, 180 size_t n_teams, 181 const struct game *games, 182 size_t n_games, 183 uint32_t otprob, 184 size_t iterations, 185 struct rand_t *seedp) 186 { 187 struct standing s0[n_teams]; 188 struct standing standings[n_teams]; 189 struct standing antistandings[n_teams]; 190 size_t i; 191 192 for (i = 0; i < n_teams; i++) { 193 s0[i].ti = i; 194 s0[i].points = teams[i].points; 195 s0[i].wins = teams[i].wins; 196 s0[i].games = teams[i].games; 197 } 198 199 for (i = 0; i < iterations / 2; i++) { 200 size_t j; 201 202 memcpy(standings, s0, sizeof(standings)); 203 memcpy(antistandings, s0, sizeof(antistandings)); 204 205 for (j = 0; j < n_games; j++) { 206 size_t wi; 207 size_t li; 208 size_t awi; 209 size_t ali; 210 211 bool antihw; 212 bool antiot; 213 bool hw = urand(games[j].expected, &antihw, seedp); 214 bool ot = urand(otprob, &antiot, seedp); 215 216 wi = games[j].t[!hw]; 217 li = games[j].t[hw]; 218 219 awi = games[j].t[!antihw]; 220 ali = games[j].t[antihw]; 221 222 standings[wi].points += 3 - ot; 223 standings[li].points += ot; 224 standings[wi].wins += !ot; 225 standings[wi].games++; 226 standings[li].games++; 227 228 antistandings[awi].points += 3 - ot; 229 antistandings[ali].points += ot; 230 antistandings[awi].wins += !ot; 231 antistandings[awi].games++; 232 antistandings[ali].games++; 233 } 234 235 for (j = 0; j < n_teams; j++) { 236 standings[j].random = getrand(seedp) % (INT_MAX / 2); 237 antistandings[j].random = getrand(seedp) % (INT_MAX / 2); 238 } 239 isort(standings, n_teams, sizeof(*standings), pointscmp); 240 241 for (j = 0; j < n_teams; j++) { 242 teams[standings[j].ti].pointsum += standings[j].points; 243 teams[standings[j].ti].gamessum += standings[j].games; 244 teams[standings[j].ti].poscounts[j]++; 245 } 246 247 isort(antistandings, n_teams, sizeof(*antistandings), pointscmp); 248 249 for (j = 0; j < n_teams; j++) { 250 teams[antistandings[j].ti].pointsum += antistandings[j].points; 251 teams[antistandings[j].ti].gamessum += antistandings[j].games; 252 teams[antistandings[j].ti].poscounts[j]++; 253 } 254 } 255 256 } 257 258 void simulate_winall(struct team *teams, 259 size_t n_teams, 260 const struct game *games, 261 size_t n_games, 262 uint32_t otprob, 263 size_t iterations, 264 size_t team_id, 265 bool win, 266 struct rand_t *seedp) 267 { 268 struct team teams_cpy[n_teams]; 269 struct game games_cpy[n_games]; 270 271 size_t r; 272 size_t w; 273 274 memcpy(teams_cpy, teams, sizeof(teams_cpy)); 275 276 for (w = r = 0; r < n_games; r++) { 277 if (games[r].t[0] == team_id || games[r].t[1] == team_id) { 278 size_t opp = games[r].t[0] + games[r].t[1] - team_id; 279 teams_cpy[team_id].points += win * 3; 280 teams_cpy[team_id].games++; 281 teams_cpy[team_id].wins += win; 282 283 teams_cpy[opp].points += !win * 3; 284 teams_cpy[opp].games++; 285 teams_cpy[opp].wins += !win; 286 } else { 287 games_cpy[w++] = games[r]; 288 } 289 } 290 291 simulate(teams_cpy, n_teams, games_cpy, w, otprob, iterations, seedp); 292 } 293 294 void simulate_winloseall(struct team *teams, 295 size_t n_teams, 296 size_t team, 297 const struct game *games, 298 size_t n_games, 299 uint32_t otprob, 300 size_t iterations, 301 struct rand_t *seedp) 302 { 303 struct team teams_cpy[n_teams]; 304 size_t poscounts[n_teams][n_teams]; 305 size_t e; 306 size_t i; 307 308 memcpy(teams_cpy, teams, sizeof(teams_cpy)); 309 memset(poscounts, 0, sizeof(poscounts)); 310 311 for (i = 0; i < n_teams; i++) 312 teams_cpy[i].poscounts = poscounts[i]; 313 314 simulate_winall(teams_cpy, n_teams, 315 games, n_games, 316 otprob, 317 iterations, 318 team, true, 319 seedp); 320 simulate_winall(teams_cpy, n_teams, 321 games, n_games, 322 otprob, 323 iterations, 324 team, false, 325 seedp); 326 327 for (i = 0; i < n_teams; i++) 328 if (teams_cpy[team].poscounts[i]) 329 break; 330 for (e = n_teams; e > 0; e--) 331 if (teams_cpy[team].poscounts[e - 1]) 332 break; 333 for (; i < e; i++) 334 teams[team].poscounts[i] = 1; 335 } 336 337 struct thread_data { 338 struct team *teams; 339 bool *winloseall; 340 size_t n_teams; 341 342 struct game *games; 343 size_t n_games; 344 345 uint32_t otprob; 346 347 size_t iterations; 348 size_t iterated; 349 pthread_mutex_t mutex; 350 }; 351 352 void *simulate_thread(void *data) 353 { 354 struct thread_data *td = data; 355 struct team teams_cpy[td->n_teams]; 356 size_t i; 357 size_t iters; 358 size_t poscounts[td->n_teams][td->n_teams]; 359 size_t n = 0; 360 struct rand_t seedp; 361 362 getseed(&seedp); 363 364 pthread_mutex_lock(&td->mutex); 365 memcpy(teams_cpy, td->teams, sizeof(teams_cpy)); 366 pthread_mutex_unlock(&td->mutex); 367 368 memset(poscounts, 0, sizeof(poscounts)); 369 370 for (i = 0; i < td->n_teams; i++) { 371 teams_cpy[i].poscounts = poscounts[i]; 372 pthread_mutex_lock(&td->mutex); 373 if (!td->winloseall[i]) { 374 td->winloseall[i] = true; 375 pthread_mutex_unlock(&td->mutex); 376 377 simulate_winloseall(teams_cpy, td->n_teams, i, 378 td->games, td->n_games, 379 td->otprob, 380 td->iterations / td->n_teams / 2, 381 &seedp); 382 } else { 383 pthread_mutex_unlock(&td->mutex); 384 } 385 } 386 387 for (;;) { 388 pthread_mutex_lock(&td->mutex); 389 390 if (td->iterations - td->iterated > 100000) 391 iters = 100000; 392 else 393 iters = td->iterations - td->iterated; 394 395 td->iterated += iters; 396 pthread_mutex_unlock(&td->mutex); 397 398 if (!iters) 399 break; 400 n += iters; 401 402 simulate(teams_cpy, td->n_teams, 403 td->games, td->n_games, 404 td->otprob, 405 iters, &seedp); 406 } 407 408 pthread_mutex_lock(&td->mutex); 409 for (i = 0; i < td->n_teams; i++) { 410 size_t j; 411 td->teams[i].pointsum += teams_cpy[i].pointsum; 412 td->teams[i].gamessum += teams_cpy[i].gamessum; 413 414 for (j = 0; j < td->n_teams; j++) 415 td->teams[i].poscounts[j] += teams_cpy[i].poscounts[j]; 416 } 417 pthread_mutex_unlock(&td->mutex); 418 419 return (void *)n; 420 } 421 422 const char *ltrim(const char *s) 423 { 424 while (*s++ == ' '); 425 return s - 1; 426 } 427 428 int main(int argc, char **argv) 429 { 430 int opt; 431 double homeadv = 58; 432 double elok = 32; 433 uint32_t otprob = 0.23 * R_MAX; 434 size_t n; 435 size_t m; 436 size_t i; 437 size_t j; 438 size_t iterations = 1000000; 439 size_t threads = 1; 440 441 while ((opt = getopt(argc, argv, "i:t:a:o:")) != -1) { 442 switch (opt) { 443 case 'i': 444 iterations = strtoull(optarg, NULL, 10); 445 break; 446 447 case 't': 448 threads = strtoull(optarg, NULL, 10); 449 break; 450 451 case 'a': 452 homeadv = strtod(optarg, NULL); 453 break; 454 455 case 'o': 456 otprob = strtod(optarg, NULL) * R_MAX; 457 break; 458 459 case 'k': 460 elok = strtod(optarg, NULL); 461 break; 462 463 default: 464 exit(1); 465 break; 466 } 467 } 468 469 iterations += iterations & 1; 470 471 if (scanf(" %zu", &n) < 1) 472 return 1; 473 474 struct team teams[n]; 475 size_t teammap[n]; 476 size_t poscounts[n][n]; 477 memset(poscounts, 0, sizeof(poscounts)); 478 479 for (i = 0; i < n; i++) { 480 if (scanf(" %127s", teams[i].name) < 1) 481 return 1; 482 teams[i].id = i; 483 teams[i].points = 0; 484 teams[i].games = 0; 485 teams[i].wins = 0; 486 teams[i].pointsum = 0; 487 teams[i].gamessum = 0; 488 teams[i].poscounts = poscounts[i]; 489 teams[i].elo = 2000; 490 } 491 492 if (scanf(" %zu", &m) < 1) 493 return 1; 494 495 for (i = 0; i < m; i++) { 496 size_t hi, ai; 497 unsigned hs, as; 498 bool ot = false; 499 char det[16] = ""; 500 501 switch (scanf(" %zu %zu %u %u%15[^\n]", &hi, &ai, &hs, &as, det)) { 502 case 5: 503 ot = !strcmp(ltrim(det), "OT") || 504 !strcmp(ltrim(det), "SO"); 505 break; 506 507 case 4: 508 break; 509 510 default: 511 exit(1); 512 } 513 514 double expected = 1. / (1 + pow(10, (teams[ai].elo - teams[hi].elo - homeadv) / 400.0)); 515 double actual; 516 517 teams[hi].games++; 518 teams[ai].games++; 519 520 if (hs > as) { 521 if (!ot) { 522 actual = 1; 523 teams[hi].wins++; 524 teams[hi].points += 3; 525 } else { 526 actual = 2./3; 527 teams[hi].points += 2; 528 teams[ai].points += 1; 529 } 530 } else { 531 if (ot) { 532 actual = 1./3; 533 teams[hi].points += 1; 534 teams[ai].points += 2; 535 } else { 536 teams[ai].points += 3; 537 teams[ai].wins++; 538 actual = 0; 539 } 540 } 541 542 teams[hi].elo += elok * (actual - expected); 543 teams[ai].elo -= elok * (actual - expected); 544 } 545 546 isort(teams, 1[&teams] - teams, sizeof(*teams), teampointscmp); 547 548 for (i = 0; i < n; i++) 549 teammap[teams[i].id] = i; 550 551 if (scanf(" %zu", &m) < 1) 552 return 1; 553 554 struct game games[m]; 555 556 for (i = 0; i < m; i++) { 557 size_t h, a; 558 if (scanf(" %zu %zu", &h, &a) < 2) 559 return 1; 560 games[i].t[0] = teammap[h]; 561 games[i].t[1] = teammap[a]; 562 games[i].expected = R_MAX / (1 + pow(10.0, (teams[games[i].t[1]].elo - (teams[games[i].t[0]].elo + homeadv)) / 400.0)); 563 } 564 565 if (threads > 1) { 566 pthread_t pthrds[threads]; 567 bool winloseall[n]; 568 memset(winloseall, 0, sizeof(winloseall)); 569 struct thread_data td = { 570 teams, winloseall, n, 571 games, m, 572 otprob, 573 iterations, 0, 574 PTHREAD_MUTEX_INITIALIZER 575 }; 576 for (i = 0; i < threads; i++) 577 pthread_create(&pthrds[i], NULL, simulate_thread, &td); 578 iterations = 0; 579 for (i = 0; i < threads; i++) { 580 void *a; 581 pthread_join(pthrds[i], &a); 582 iterations += (size_t)a; 583 } 584 } else { 585 struct rand_t seedp; 586 getseed(&seedp); 587 588 for (i = 0; i < n; i++) { 589 simulate_winloseall(teams, n, i, 590 games, m, 591 otprob, 592 iterations / n / 2, 593 &seedp); 594 } 595 596 simulate(teams, n, 597 games, m, 598 otprob, 599 iterations, 600 &seedp); 601 } 602 603 isort(teams, n, sizeof(*teams), teamcmp); 604 for (i = 0; i < n; i++) { 605 printf("%s %u %u %llu %.1f %d", teams[i].name, 606 teams[i].games, teams[i].points, 607 teams[i].gamessum / iterations, 608 (double)teams[i].pointsum / iterations, 609 (int)round(teams[i].elo)); 610 for (j = 0; j < n; j++) { 611 if (teams[i].poscounts[j] == iterations + 1) 612 printf(" +"); 613 else if (teams[i].poscounts[j]) 614 printf(" %e", (teams[i].poscounts[j] - 1) * 100.0 / iterations); 615 else 616 printf(" -"); 617 } 618 printf("\n"); 619 } 620 621 return 1; 622 }