Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_check_alchemy_c = 00003 * "$Id: check_alchemy.c 4640 2006-06-07 21:44:18Z tchize $"; 00004 */ 00005 00006 /* 00007 * CrossFire, A Multiplayer game for X-windows 00008 * 00009 * Copyright (C) 2007 Mark Wedel & Crossfire Development Team 00010 * Copyright (C) 1992 Frank Tore Johansen 00011 * 00012 * This program is free software; you can redistribute it and/or modify 00013 * it under the terms of the GNU General Public License as published by 00014 * the Free Software Foundation; either version 2 of the License, or 00015 * (at your option) any later version. 00016 * 00017 * This program is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU General Public License for more details. 00021 * 00022 * You should have received a copy of the GNU General Public License 00023 * along with this program; if not, write to the Free Software 00024 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00025 * 00026 * The authors can be reached via e-mail at crossfire-devel@real-time.com 00027 */ 00028 00029 /* 00030 * This tests the comet spell. My main motivation for writing this 00031 * was to have a consistent test I could use for performance testing. 00032 * But I also wanted to make sure that the results were close before and 00033 * after the performance changes - make the spell use less cpu time 00034 * but having drastically different results probably isn't a good thing 00035 * either. 00036 * To really be useful, everything needs to be compiled with profiling 00037 * (-pg). This can be done like 'setenv CFLAGS -pg; ./configure; 00038 * make clean; make'. The make clean is necessary because it won't 00039 * recompile the objects based just on changes the the CFLAGS. 00040 * 00041 * Note that this test, even after performance changes are done, still 00042 * isn't bad - it checks several things - map creation, spell casting, 00043 * etc. It wouldn't be hard to use this as a template to test things 00044 * like resistance code, etc. 00045 */ 00046 00047 #include <stdlib.h> 00048 #include <check.h> 00049 00050 #include <global.h> 00051 #include <sproto.h> 00052 00053 #define TEST_MAP_SIZE 40 00054 #define NUM_TICKS_TO_RUN 500 00055 #define NUM_COMETS_TO_CAST 30 00056 #define STARTING_HP 25000 00057 00058 /* The percentage, either plus or minus, that the results can 00059 * vary from the baseline and still be considered OK. 00060 * Note a good sanity check to make sure you put in the correct 00061 * values is to set this to 0.0 - in that case, checks should 00062 * pass. 00063 */ 00064 #define HP_DELTA 5 00065 00066 /* The first time you set up a test, you want to dump the 00067 * initial values to put into the hp_row/hp_diag arrays. 00068 * If this is set, it prints those values instead of doing 00069 * a comparision. 00070 */ 00071 /*#define PRINT_DEBUG_HP */ 00072 00073 mapstruct *test_map; 00074 object *mon; 00075 00076 void setup(void) { 00077 object *mon1; 00078 int x, i; 00079 00080 test_map = get_empty_map(TEST_MAP_SIZE, TEST_MAP_SIZE); 00081 00082 mon = create_archetype("orc"); 00083 fail_unless(mon != NULL, "Unable to find orc object"); 00084 00085 /* We customize the monster a bit so it is consistent - 00086 * give it a bunch of HP so it can survive the attacks, 00087 * set it speed to 0 so it doesn't do anything, etc. 00088 */ 00089 for (i = 0; i < NROFATTACKS; i++) 00090 mon->resist[i] = 95; 00091 mon->stats.hp = STARTING_HP; 00092 mon->stats.maxhp = STARTING_HP; 00093 mon->speed = 0.0; 00094 mon->speed_left = 0.0; 00095 SET_FLAG(mon, FLAG_STAND_STILL); 00096 update_ob_speed(mon); 00097 00098 /* We now make copies of this custom monster and put it into the 00099 * map. We make a diagonal from one corner to another, 00100 * as well as a line across the middle of the map (\ overlayed with -) 00101 * We could fill most of the map with monsters, but I think the 00102 * diagonal + horizontal should give a pretty representative 00103 * value of creatures being hit. 00104 */ 00105 for (x = 0; x < TEST_MAP_SIZE; x++) { 00106 mon1 = get_object(); 00107 copy_object(mon, mon1); 00108 mon1->x = x; 00109 mon1->y = TEST_MAP_SIZE/2; 00110 mon1->map = test_map; 00111 insert_ob_in_map(mon1, mon1->map, NULL, 0); 00112 00113 if (x != TEST_MAP_SIZE/2) { 00114 mon1 = get_object(); 00115 copy_object(mon, mon1); 00116 mon1->x = x; 00117 mon1->y = x; 00118 mon1->map = test_map; 00119 insert_ob_in_map(mon1, mon1->map, NULL, 0); 00120 } 00121 } 00122 00123 } 00124 00125 void teardown(void) { 00126 free_map(test_map); 00127 } 00128 00129 void check_hp(const char *test, int hp_row[TEST_MAP_SIZE], int hp_diag[TEST_MAP_SIZE]) { 00130 object *our_mon; 00131 int x, diff; 00132 00133 #ifdef PRINT_DEBUG_HP 00134 printf("\nDumping debug hp for test %s\n ", test); 00135 #endif 00136 00137 /* Dump the HP of the monsters now. We do it in 2 passes, 00138 * as I think it is easier to read that way. 00139 */ 00140 for (x = 0; x < TEST_MAP_SIZE; x++) { 00141 our_mon = GET_MAP_OB(test_map, x, TEST_MAP_SIZE/2); 00142 if (!our_mon) { 00143 fail("Monster destroyed at %d, %d\n", x, TEST_MAP_SIZE/2); 00144 continue; 00145 } 00146 fail_unless(mon->name == our_mon->name, "Could not find our monster on the space?"); 00147 00148 #ifdef PRINT_DEBUG_HP 00149 printf("%d, ", our_mon->stats.hp); 00150 #else 00151 00152 if (our_mon->stats.hp == hp_row[x]) { 00153 diff = 0; 00154 } else if (our_mon->stats.hp < hp_row[x]) { 00155 diff = 100-(STARTING_HP-hp_row[x])*100/((STARTING_HP-our_mon->stats.hp) ? (STARTING_HP-our_mon->stats.hp) : 1); 00156 } else { 00157 diff = -(100-(STARTING_HP-our_mon->stats.hp)*100/((STARTING_HP-hp_row[x]) ? (STARTING_HP-hp_row[x]) : 1)); 00158 } 00159 00160 if (FABS(diff) > HP_DELTA) { 00161 fail("Mon (%d, %d) has hp out of range (%d != %d +/- %d, diff %d)\n", our_mon->x, our_mon->y, our_mon->stats.hp, hp_row[x], HP_DELTA, diff); 00162 } 00163 #endif 00164 } 00165 00166 #ifdef PRINT_DEBUG_HP 00167 printf("\n\n"); 00168 #endif 00169 00170 for (x = 0; x < TEST_MAP_SIZE; x++) { 00171 our_mon = GET_MAP_OB(test_map, x, x); 00172 if (!our_mon) { 00173 fprintf(stderr, "Monster destroyed at %d, %d\n", x, x); 00174 continue; 00175 } 00176 00177 fail_unless(mon->name == our_mon->name, "Could not find our monster on the space?"); 00178 00179 #ifdef PRINT_DEBUG_HP 00180 printf("%d, ", our_mon->stats.hp); 00181 #else 00182 if (our_mon->stats.hp == hp_diag[x]) { 00183 diff = 0; 00184 } else if (our_mon->stats.hp < hp_diag[x]) { 00185 diff = 100-(STARTING_HP-hp_diag[x])*100/((STARTING_HP-our_mon->stats.hp) ? (STARTING_HP-our_mon->stats.hp) : 1); 00186 } else { 00187 diff = -(100-(STARTING_HP-our_mon->stats.hp)*100/((STARTING_HP-hp_diag[x]) ? (STARTING_HP-hp_diag[x]) : 1)); 00188 } 00189 00190 if (FABS(diff) > HP_DELTA) { 00191 fail("Mon (%d, %d) has hp out of range (%d != %d +/- %d, diff %d)\n", our_mon->x, our_mon->y, our_mon->stats.hp, hp_diag[x], HP_DELTA, diff); 00192 } 00193 #endif 00194 } 00195 } 00196 00197 START_TEST(cast_one_comet) { 00198 int hp_row[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 24895, 24890, 24885, 24880, 24875, 24870, 24865, 24860, 24855, 24850, 24845, 24840, 24827, 24840, 24845, 24850, 24855, 24860, 24865, 24870, 24875, 24880, 24885, 24890, 24895, 25000, 25000, 25000, 25000, 25000, 25000, 25000 }, 00199 hp_diag[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 24895, 24890, 24885, 24880, 24875, 24870, 24865, 24860, 24855, 24850, 24845, 24840, 24827, 24840, 24845, 24850, 24855, 24860, 24865, 24870, 24875, 24880, 24885, 24890, 24895, 25000, 25000, 25000, 25000, 25000, 25000, 25000 }; 00200 object *comet, *rod; 00201 int tick; 00202 00203 rod = create_archetype("rod_heavy"); 00204 rod->level = 100; 00205 comet = create_archetype("spell_comet"); 00206 insert_ob_in_ob(comet, rod); 00207 00208 rod->map = test_map; 00209 rod->x = TEST_MAP_SIZE/2; 00210 rod->y = TEST_MAP_SIZE-1; 00211 00212 insert_ob_in_map(rod, rod->map, NULL, 0); 00213 00214 cast_spell(rod, rod, 1, rod->inv, NULL); 00215 for (tick = 0; tick < NUM_TICKS_TO_RUN; tick++) { 00216 process_events(); 00217 } 00218 00219 check_hp("cast_one_comet", hp_row, hp_diag); 00220 } 00221 END_TEST 00222 00223 START_TEST(cast_random_comet) { 00224 object *comet, *rod; 00225 int tick, num_cast = 0; 00226 int hp_row[TEST_MAP_SIZE] = {23522, 23380, 23217, 23172, 23137, 23007, 22882, 22762, 22655, 22527, 22412, 22407, 22307, 22312, 22217, 22235, 22235, 22217, 22312, 22412, 22412, 22470, 22620, 22770, 22915, 23060, 23200, 23335, 23365, 23400, 23535, 23670, 23800, 23930, 24055, 24180, 24200, 24325, 24445, 24565 }, 00227 hp_diag[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 23215, 23025, 22830, 22745, 22570, 22510, 22360, 22315, 22280, 22255, 22340, 22425, 22412, 22312, 22317, 22330, 22317, 22417, 22522, 22632, 22647, 22672, 22815, 22945, 23062, 23192, 23327, 23467, 23512, 23675, 23825, 23970 }; 00228 00229 rod = create_archetype("rod_heavy"); 00230 rod->level = 100; 00231 comet = create_archetype("spell_comet"); 00232 insert_ob_in_ob(comet, rod); 00233 00234 rod->map = test_map; 00235 rod->x = TEST_MAP_SIZE/2; 00236 rod->y = TEST_MAP_SIZE-1; 00237 00238 insert_ob_in_map(rod, rod->map, NULL, 0); 00239 00240 for (tick = 0; tick < NUM_TICKS_TO_RUN; tick++) { 00241 if (num_cast < NUM_COMETS_TO_CAST && (tick%1) == 0) { 00242 remove_ob(rod); 00243 00244 /* The idea here on the x is to shuffle the spaces around 00245 * a little, as a more typical case is comets 00246 * blowing up on different spaces. 00247 */ 00248 rod->x = (tick*59)%37; 00249 rod->y = TEST_MAP_SIZE-1; 00250 rod->map = test_map; 00251 insert_ob_in_map(rod, rod->map, NULL, 0); 00252 00253 cast_spell(rod, rod, 1, rod->inv, NULL); 00254 num_cast++; 00255 } 00256 process_events(); 00257 } 00258 check_hp("cast_random_comet", hp_row, hp_diag); 00259 00260 } 00261 END_TEST 00262 00263 START_TEST(cast_bunch_comet) { 00264 object *comet, *rod; 00265 int tick, num_cast = 0; 00266 int hp_row[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 21850, 21700, 21550, 21400, 21250, 21100, 20950, 20800, 20650, 20500, 20350, 20200, 19810, 20200, 20350, 20500, 20650, 20800, 20950, 21100, 21250, 21400, 21550, 21700, 21850, 25000, 25000, 25000, 25000, 25000, 25000, 25000 }, 00267 hp_diag[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 21850, 21700, 21550, 21400, 21250, 21100, 20950, 20800, 20650, 20500, 20350, 20200, 19810, 20200, 20350, 20500, 20650, 20800, 20950, 21100, 21250, 21400, 21550, 21700, 21850, 25000, 25000, 25000, 25000, 25000, 25000, 25000 }; 00268 00269 rod = create_archetype("rod_heavy"); 00270 rod->level = 100; 00271 comet = create_archetype("spell_comet"); 00272 insert_ob_in_ob(comet, rod); 00273 00274 rod->map = test_map; 00275 rod->x = TEST_MAP_SIZE/2; 00276 rod->y = TEST_MAP_SIZE-1; 00277 00278 insert_ob_in_map(rod, rod->map, NULL, 0); 00279 00280 for (tick = 0; tick < NUM_TICKS_TO_RUN; tick++) { 00281 if (num_cast < NUM_COMETS_TO_CAST && (tick%1) == 0) { 00282 cast_spell(rod, rod, 1, rod->inv, NULL); 00283 num_cast++; 00284 } 00285 process_events(); 00286 } 00287 check_hp("cast_bunch_comet", hp_row, hp_diag); 00288 00289 } 00290 END_TEST 00291 00292 Suite *comet_suite(void) { 00293 Suite *s = suite_create("comet"); 00294 TCase *tc_core = tcase_create("Core"); 00295 00296 /* check by defaults has a 2 second timeout - that isn't 00297 * fast enough on my system - a run of 30 comets takes about 00298 * 7 seconds. Setting this to 20 is enough, but on a slower 00299 * system may not be. 00300 */ 00301 tcase_set_timeout(tc_core, 20); 00302 00303 /*setup and teardown will be called before each test in testcase 'tc_core' */ 00304 tcase_add_checked_fixture(tc_core, setup, teardown); 00305 00306 suite_add_tcase(s, tc_core); 00307 tcase_add_test(tc_core, cast_one_comet); 00308 tcase_add_test(tc_core, cast_bunch_comet); 00309 tcase_add_test(tc_core, cast_random_comet); 00310 00311 return s; 00312 } 00313 00314 int main(void) { 00315 int nf; 00316 00317 Suite *s = comet_suite(); 00318 SRunner *sr = srunner_create(s); 00319 00320 /* Don't want to fork - if we do, we lose the profile (-pg) 00321 * compiled information, which is what I used to determine if 00322 * things are more efficient. 00323 */ 00324 srunner_set_fork_status(sr, CK_NOFORK); 00325 00326 /* Only want to run this once, so don't put it in setup() */ 00327 init(0, NULL); 00328 00329 srunner_set_xml(sr, LOGDIR "/unit/server/comet.xml"); 00330 srunner_set_log(sr, LOGDIR "/unit/server/comet.out"); 00331 srunner_run_all(sr, CK_ENV); /*verbosity from env variable*/ 00332 nf = srunner_ntests_failed(sr); 00333 srunner_free(sr); 00334 fprintf(stderr, "Got %"FMT64U" supressions, %"FMT64U" spell merges, %"FMT64U" full tables\n", statistics.spell_suppressions, statistics.spell_merges, statistics.spell_hash_full); 00335 return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 00336 }