#ifndef lint static char *rcsid = "$Header: /tmp_mnt/vida/disks/disk5/Users/terry/r/gassy/RCS/template_main.c,v 1.3 1992/10/09 06:43:50 terry Exp terry $"; #endif /* * You must include types.h * * types.h contains definitions of all the types used in Gassy. * Your application should make use of these too. Doing so will * make it easier for your code to be ported to different * architectures. * * Some important types in types.h are discussed below. Their contents * are not always the same, it depends on what your application * looks like. For example, if your individuals are not necessarily * all the same size, the INDIVIDUAL type contains a genome_size * field that is not present if your individuals have constant size. * The descriptions below are largely for people who have a "normal" * GA running. By normal I mean one that uses fixed length character * strings as individuals and a fixed sized population etc. * * The fields that are included for any given type are determined * based on the contents of application.h in each application directory. * This file is created by the shell script new_app which you ran to * create this directory and also this file. You don't have to include * application.h anywhere, types.h does that already. * * The types in types.h: * ===================================================================== * BOOLEAN, this can be either TRUE or FALSE. * * FITNESS is the type that your fitness function returns. This will be * set for you, depending on your response to the question in new_app. * * INDIVIDUAL_TYPE is the type of your individuals. In the normal case this * is defined as a STRING (which, in turn, is a char *). So your application, * if it knows your individuals are strings, can treat something of type * INDIVIDUAL_TYPE as though it were a character string. * * A variable of type INDIVIDUAL contains at least 3 fields. These are * 'genome' which is of type INDIVIDUAL_TYPE, 'fitness' which is of type * FITNESS, and 'evaluated' which is of type BOOLEAN. Normally you only * need to worry about the genome field, but in case applications want to * be more sophisticated, a pointer to an individual is normally passed * to application routines that deal with individuals. * * A POPULATION is simply an array of INDIVIDUALS. * * A CONTEXT contains all sorts of goodies that are to do with a run. * Important members of this structure are population_size, genome_size * (for applications with fixed-sized individuals), the total fitness * of the generation, the index in the population of the best individual * of the generation, that fitness, the generation number, all the * probabilities associated with crossover, mutation, etc., information * about the best individual found so far, and more. Almost all application * functions will be passed a pointer to the current run context. Most * will only look at the population, but everything is provided for * applications that may want to modify thing - like the population size, * the muattion probability etc. * * A LOCUS is a structure describing the possible alleles and their * frequency of occurence at some locus on the genome. If you are writing * an application that uses fixed-length binary strings, you will never * have to deal with this type. Otherwise, if you are using fixed-length * strings, you will need to tell Gassy what the possible alleles are at each * locus. If the probability of occurence of each allele at a locus is not * the same, you will also need to give information on the probabilities. * Then Gassy will know how to create random individuals for the initial * population and how to mutate an individual for you. * * An OPTION is the structure used to describe possible command line * options. You will never have to deal with this type if no don't have * any command line arguments. An OPTION has ten fields. These are * explained below (if your application has any options). * * If your application usues options, it has only to declare the variables * that will contain the values assigned to the options on the command line, * and declare an array of type OPTION, with one entry per option (and an * empty final option to mark the end of all options). The main program * will deal with reading the command line, checking option limits and * checking for options that are mandatory. You can even have options * that result in functions being called - this is how the usage message * is produced when the -usage option is used. For an example of how * to define an option, look in the file max/ones/max_ones.c which defines * a single 'length' option. The main program has a plethora (and I think * that word is justified) of options. They can be found in main_options.c * and are listed by any application when the -usage option is given. * * The type FUNCTION is used to specify a function. Your application may * define several functions - for instance to do your own application * specific crossover or mutation. For each of these it must declare * an array of FUNCTIONs. This type consists of * * 1) The name of the function. This is the name that the user * will give to reference the function from the command line. * 2) The address of the function. You just have to give the name * of the function, which must already have been declared as * returning a VOID. * 3) The address of an initialization function. This may be empty, * if not, it will be called before the above function is ever * called and it will be passed the run time context. * 4) An explanation of what your function does, for display with * the -functions option. * * For an example of how to do this, look in busy_beavers/busy_beavers.c * */ #if ! defined(APP_HAS_COMMAND_LINE_OPTIONS) INCLUDE "types.h" #else INCLUDE "types.h" INCLUDE "options.h" #endif #if defined(APP_HAS_COMMAND_LINE_OPTIONS) /* * Setting up command line options for use with your application requires * only that you declare the variable associated with the option, and that * you make an entry in the array below which describes the properties * of your option. The OPTION type has ten fields. This may seem * excessive, but it is designed to take all the work of option processing * out of your hands. The fields are: * * 1) The long form of the name of the option. * 2) An abbreviated name for the option (if desired). * 3) The type of the option (see types.h). * 4) Whether the option is mandatory. * 5) Is there a lower bound on this option? * 6) If so, what is the lower bound? * 7) Is there an upper bound on this option? * 8) If so, what is the upper bound? * 9) The address of something to operate on when the option * is found (the action is determined by the option type). * 10) A USEFUL help message. * * The long and abbreviated names are the ones that the user will use * on the command line. They may both be abbreviated in actual use. * The type is one of BOOLEAN_OPT, CHAR_OPT, STRING_OPT, INT_OPT, * FLOAT_OPT, DOUBLE_OPT, LONG_OPT, FUNC0_OPT, FUNC1_OPT, FITNESS_OPT. * Most of these should be obvious. The option type should correspond * to the type of your option variable. The type will be used in * assigning a value from the command ine to the variable address you * give in field (9). * * Fields (5) through (8) are used to give bounds on the values that * can be assigned to the corresponding variable. Bounds checking is * only done on the numeric types. There are a couple of #defines in * options.c that are useful for defining these limits. * * Boolean options do not expect an argument, in which case their * state is toggled, but they can be given one, any of yes/no, * true/false, 1/0 is recognized. A FUNC0 type will result in the * function whose address is given in field (9) being called with no * arguments. A FUNC1 type will call the function with a single * argument, taken from the command line. As mentioned above, see the * file main_options.c in the Gassy directory for a large example of * options. */ OPTION app_options[] = { /* DO NOT CHANGE THE NEXT LINE - IT MARKS THE END OF YOUR OPTIONS! */ { (STRING) 0, "", 0, FALSE, NO_LIMITS(), (VOID *) 0, "" } }; #if defined(APP_WANTS_ADDITIONAL_OPTION_CHECKING) /* * Here you have a chance to do further checking of options if you need * more than a check on mandatory option existence and bounds checking. * For example, you might want to check for certain combinations of * options. This function will be called after all command line arguments * have been processed. */ VOID app_check_options() { } #endif #endif #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) /* * Return the length of your individuals. This will be used by the main * progranm to allocate space for the population etc. Because you told the * new_app program that your individuals were of constant size, Gassy will * be expecting the value you return here to always be true! */ INT app_individual_size() { } #endif #if defined(APP_NEEDS_TO_INITIALIZE) /* * Do any other setting up you might need to do before the main * program can call your app_evaluate() function. For example, you * might want to initialize some statistics keeping variables or get * some memory with Malloc for your own devious purposes. */ VOID app_initialize(context) CONTEXT *context; { } #endif #if defined(APP_NOISY_FITNESS_FUNCTION) /* * Get the fitness for each individual in the population. You should * put the index of the best individual of the generation into the * variable context->best_individual_of_generation and put that * fitness value into context->best_fitness_of_generation. Each * individual's fitness should be put into its fitness field. * The total fitness should be put into context->total_fitness. * The code to do most of this is provided for you. You just need * to fill in the code at the comment "EVALUATE". The genome * of the individual is in context->population[i]->genome. */ VOID app_evaluate_population(context) CONTEXT *context; { register INT i; register INT population_size = context->population_size; register FITNESS this_fitness; context->total_fitness = (FITNESS)0; context->best_fitness_of_generation = WORST_FITNESS; for (i = 0; i < population_size; i++){ register FITNESS this_fitness; this_fitness = context->population[i]->unscaled_fitness = /* EVALUATE */ context->total_fitness += this_fitness; /* * Watch for the best individual of the generation. */ #if defined(APP_LOW_FITNESS_IS_BETTER) if (this_fitness < context->best_fitness_of_generation){ #else if (this_fitness > context->best_fitness_of_generation){ #endif } return; } #else #if defined(APP_EVALUATES_ENTIRE_POPULATION) /* * Get the fitness for each individual in the population. You should * put the index of the best individual of the generation into the * variable context->best_individual_of_generation and put that * fitness value into context->best_fitness_of_generation. Each * individual's fitness should be put into its fitness field. * The total fitness should be put into context->total_fitness. * You should also check to see if the fitness of the individual * is already known and not evaluate it if so. * * The code to do almost all of this is provided for you. You just * need to fill in the code at the comment "EVALUATE". The genome * of the individual is in context->population[i]->genome. */ VOID app_evaluate_population(context) CONTEXT *context; { register INT i; register INT population_size = context->population_size; register FITNESS this_fitness; context->total_fitness = (FITNESS)0; context->best_fitness_of_generation = WORST_FITNESS; for (i = 0; i < population_size; i++){ register FITNESS this_fitness; /* Check for individuals whose fitness is already known. */ if (context->population[i]->evaluated == FALSE){ this_fitness = context->population[i]->unscaled_fitness = /* EVALUATE */ context->population[i]->evaluated = TRUE; } else { this_fitness = context->population[i]->unscaled_fitness; } context->total_fitness += this_fitness; /* * Watch for the best individual of the generation. */ if (this_fitness #if defined(APP_LOW_FITNESS_IS_BETTER) if (this_fitness < context->best_fitness_of_generation){ #else if (this_fitness > context->best_fitness_of_generation){ #endif context->best_individual_of_generation = i; context->best_fitness_of_generation = this_fitness; } } return; } #else /* * Return the fitness of this individual. The genome of the individual * is in context->population[who]->genome. This is of type * INDIVIDUAL_TYPE (commonly char *). */ FITNESS app_evaluate_individual(who, context) INT who; CONTEXT *context; { } #endif #endif #if ! defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) || ! defined(APP_INDIVIDUALS_ARE_STRINGS) /* * Create some randoms individual. They should be put into * context->population, starting at index start_index and continuing * to context->population[context->population_size - 1]. Seeing as your * individuals are not constant size, the main program made each * individual's genome be of type INDIVIDUAL_TYPE, but went no * further in terms of memory allocation. So if your INDIVIDUAL_TYPE * contains pointers to things that are supposed to contain pieces * of your genome, you will need to get that space here too. If your * INDIVIDUAL_TYPE does not contain pointers to objects that need to * be allocated, you probably should have told new_app that your * individuals were of constant size. */ VOID app_create_random_individuals(start_index, context); INT start_index; CONTEXT *context; { } #endif #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) && defined(APP_INDIVIDUALS_ARE_STRINGS) #if defined(APP_INDIVIDUALS_ARE_BINARY) #if ! defined(APP_INDIVIDUALS_HAVE_EQUAL_PROB_ALLELES) /* * Your individuals are constant length binary strings, but the * probabilities of each allele are not the same. In this function you * need to assign a probability to each allele (in the order 0, 1) by * filling in the allele[X]->probabilities[Y] locations (these are of * type PROBABILITY, which is usually a DOUBLE, which is usually a * double, see type.h), where X ranges from 0 to the length of your * genome minus one (and you know that value already since your * strings are constant length) and Y is either 0 or 1. The space for * these two locations has already been assigned for you. */ VOID app_allele_probabilities(alleles) LOCUS *alleles; { } #endif #else #if defined(APP_INDIVIDUALS_HAVE_EQUAL_PROB_ALLELES) /* * Your individuals are constant length strings, where the * probabilities of each allele are the same. But the strings are not * binary, so here you are expected to fill in the possible allele * values at each locus on the genome. You need to make * allele[X]->possible_alleles point at a character string containing * all possible alleles, where X ranges from 0 to the length of your * genome minus one (and you know that value already since your * strings are constant length). These strings will only be read by * the main program, so it is fine to allocate a single array that * is then pointed to by more than one locus. For an example of how * you might do this with the characters 'a' through 'z', see the * example program in my_name/my_name.c. */ VOID app_allele_possibilities(alleles) LOCUS *alleles; { } #else VOID /* * Your individuals are constant length strings, where the * probabilities of each allele are not the same and the strings are not * binary. Here you are expected to fill in the possible allele * values and probabilities at each locus on the genome. You need to make * allele[X]->possible_alleles point at a character string containing * all possible alleles, where X ranges from 0 to the length of your * genome minus one (and you know that value already since your * strings are constant length). These strings will only be read by * the main program, so it is fine to allocate a single array that * is then pointed to by more than one locus. For an example of how * you might do this with the characters 'a' through 'z', see the * example program in my_name/my_name.c. * * Then you need to make allele[X]->probabilities point at an array of * type PROBABILITY (usually that is typedef'd to DOUBLE, which is * usually double, see types.h) which contains the probability of * occurence for each of the alleles you have defined for locus X. The * order of the probabilities must of course match the order of the * alleles for the locus. The space for these values has not already * been allocated since the main program has no idea how many of them * to expect at each locus. */ app_initial_population_alleles(alleles) LOCUS *alleles; { } #endif #endif #endif #if defined(APP_INDIVIDUALS_HAVE_CONSTANT_SIZE) && defined(APP_INDIVIDUALS_ARE_STRINGS) && \ defined(APP_ALLELE_PROBS_CHANGE) && ! defined(APP_ALLELE_PROBS_CHANGE_TO_EQUAL) /* * The occurence probabilities for each of the possible alleles at * each locus on your genome are not the same as those for the the * initial population. Here you are expected to assign the new * probabilities to allele[X]->probabilities[Y] locations (these are * of type PROBABILITY, which is usually a DOUBLE, which is usually a * double, see type.h), where X ranges from 0 to the length of your * genome minus one (and you know that value already since your * strings are constant length) and Y ranges over the number of * possible alleles for locus X. You know the story... */ VOID app_mutation_allele_probabilities(alleles) LOCUS *alleles; { } #endif #if defined(APP_PRINTS_STATS_EACH_GENERATION) /* * Print (to fp) anything you want displayed at the end of generations, * or do any end of generation statistical things you feel like. The * context variable contains all sorts of things you might want to * look at, such as the population, its size, the sum of all the fitnesses * of the individuals, the generation number (which starts at 1), the * best fitness and the index of an individual with that fitness. * See types.h for more details on the fields in context. * */ VOID app_generation(fp, context) FILE *fp; CONTEXT *context; { } #endif #if defined(APP_PRINTS_STATS_AT_END) /* * Print (to fp) anything you want output at the end of a run. * Note that there may be many runs in one invocation of your * application. You may of course do other things that do not * involve printing. There are plenty of things in context that * you might want to mess with between runs. */ VOID app_end_of_run(fp, context) FILE *fp; CONTEXT *context; { } /* * Print (to fp) anything you want output at the end of all runs. */ VOID app_end(fp, context) FILE *fp; CONTEXT *context; { } #endif #if ! defined(APP_INDIVIDUALS_ARE_PRINTABLE_STRINGS) /* * Print (to fp) this individual in whatever fashion you choose. Do * not end with a newline. The output will probably look neater if the * individual is printed on a single line. */ VOID app_print_individual(fp, individual) FILE *fp; INDIVIDUAL *individual; { } #endif #if defined(APP_HAS_ITS_OWN_SCALING_METHODS) /* * Define your application's scaling function(s) here. The scaling * functions are used to adjust the fitness values returned by the * evaluation function to further emphasize the differences in their * values. * * The definition of your scaling functions will normally be done * in three steps. Firstly you must (unless the function is present * in this file before this comment), forward declare your functions. * They must all return VOID. Then you set up an array of type * FUNCTION which contains a line for each of the scaling functions * you have written. The example code below should be replaced with * the actual names of your function(s). Lastly, you have to write * the function(s). */ VOID scaling_function_1(); /* * An array of your scaling function(s). Each line in the array * variable below should contain three things: * * (1) The name of your function that you would like to use on * the command line after the -scaling_selection_method option. * (2) The name of the function as it appears above and below * (where you will write the actual function). * (3) The address of an initialization function. This may be empty, * if not, it will be called before the above function is ever * called and it will be passed the run time context. * (4) A brief description of the behavior of this function * for display with the -functions option. * * Do NOT remove the last line, it marks the end of your scaling * function(s). */ FUNCTION app_scaling_functions[] = { { "cmd_line_name", scaling_function_1, init_function_1, "description" }, { (STRING) 0, (PFPV) 0, (PFV) 0, (STRING) 0 } }; /* * Here is your actual scaling function. */ VOID scaling_function_1(context) CONTEXT *context; { } #endif #if defined(APP_HAS_ITS_OWN_SURVIVOR_METHODS) /* * Define your application's survivor function(s) here. The survivor * functions are used to decide which individuals will be copied * unchanged from one generation to the next, in the case that the * generation gap is not 1.0. * * The definition of your survivor functions will normally be done * in three steps. Firstly you must (unless the function is present * in this file before this comment), forward declare your functions. * They must all return VOID. Then you set up an array of type * FUNCTION which contains a line for each of the survivor functions * you have written. The example code below should be replaced with * the actual names of your function(s). Lastly, you have to write * the function(s). */ VOID survivor_function_1(); /* * An array of your survivor function(s). Each line in the array * variable below should contain three things: * * (1) The name of your function that you would like to use on * the command line after the -survivor_selection_method option. * (2) The name of the function as it appears above and below * (where you will write the actual function). * (3) The address of an initialization function. This may be empty, * if not, it will be called before the above function is ever * called and it will be passed the run time context. * (4) A brief description of the behavior of this function * for display with the -functions option. * * Do NOT remove the last line, it marks the end of your survivor * function(s). */ FUNCTION app_survivor_functions[] = { { "cmd_line_name", survivor_function_1, init_function_1, "description" }, { (STRING) 0, (PFPV) 0, (PFV) 0, (STRING) 0 } }; /* * Here is your actual survivor function. It is expected to choose * 'nsurvivors' individuals from context->population for survival * unchanged into the next generation. The indices of the * individuals you choose should be placed into the array * 'selections'. */ VOID survivor_function_1(selections, nsurvivors, context) INT *selections; INT nsurvivors; CONTEXT *context; { } #endif #if defined(APP_HAS_ITS_OWN_CROSSOVER_METHODS) /* * Define your application's crossover function(s) here. * * The definition of your crossover function(s) will normally be done in * three steps. Firstly you must (unless the function is present in * this file before this comment), forward declare your function(s). * They must all return VOID. Then you set up an array of type * FUNCTION which contains a line for each of the crossover functions * you have written. The example code below should be replaced with * the actual names of your function(s). Lastly, you have to write * the function(s). */ VOID crossover_function_1(); /* * Your crossover function(s). Each line in the array * variable below should contain three things: * * (1) The name of your function that you would like to use on * the command line after the -crossover_method option. * (2) The name of the function as it appears above and below * (where you will write the actual function). * (3) The address of an initialization function. This may be empty, * if not, it will be called before the above function is ever * called and it will be passed the run time context. * (4) A brief description of the behavior of this function * for display with the -functions option. * * Do NOT remove the last line, it marks the end of your crossover * function(s). */ FUNCTION app_crossover_functions[] = { { "cmd_line_name", crossover_function_1, init_function_1, "description" }, { (STRING) 0, (PFPV) 0, (PFV) 0, (STRING) 0 } }; /* * You must write a function that does crossover for each * of the function(s) you put in the function list above. They * will all be called with the same arguments. The function is expected * to choose individuals from context->population, the index of the * individuals that are to be used are given in the selections array * and there are 'nselections' of them. In other words, each of the * 'nselections' integers in 'selections' is the index of an individual * in context->population that has been selected for genetic * processing (in this case crossover). * * You are expected to produce nreproducers new individuals and to * put them into the first nreproducers places in new_population. * The main program doesn't care how you do this - for instance you * might not have 2 parents per child, or your crossover may only * produce one child, just as long as you produce nreproducers * new individuals, everything will work. * * To make your job easier, if you are doing some form of standard * crossover (choose 2 parents at random & create 2 children), you * might like to call the general_crossover() function in crossover.c. * You pass it the name of a function that takes pointers to two * parents and fills in two children, and also all the arguments you get * here. general_crossover() will choose parents and call your function * to create the new individuals. It takes into account the possibility * that nreproducers may not be even etc. For an example of how to * use it, see busy_beavers/busy_beavers.c or crossover.c. This will * cost you an extra fucntion call per generation, but if that bothers you * you can copy general_crossover() to here, put your crossover code * into the middle of it and rename it. */ VOID crossover_function_1(selections, nselections, nreproducers, new_population, context) INT *selections; INT nselections; INT nreproducers; POPULATION new_population; CONTEXT *context; { } #endif #if defined(APP_HAS_ITS_OWN_SELECTION_METHODS) /* * Define your application's selection function(s) here. * * The definition of your selection function(s) will normally be done in * three steps. Firstly you must (unless the function is present in * this file before this comment), forward declare your function(s). * They must all return VOID. Then you set up an array of type * FUNCTION which contains a line for each of the selection functions * you have written. The example code below should be replaced with * the actual names of your function(s). Lastly, you have to write * the function(s). */ VOID selection_function_1(); /* * Your selection function(s). Each line in the array * variable below should contain three things: * * (1) The name of your function that you would like to use on * the command line after the -selection_method option. * (2) The name of the function as it appears above and below * (where you will write the actual function). * (3) The address of an initialization function. This may be empty, * if not, it will be called before the above function is ever * called and it will be passed the run time context. * (4) A brief description of the behavior of this function * for display with the -functions option. * * Do NOT remove the last line, it marks the end of your selection * function(s). */ FUNCTION app_selection_functions[] = { { "cmd_line_name", selection_function_1, init_function_1, "description" }, { (STRING) 0, (PFPV) 0, (PFV) 0, (STRING) 0 } }; /* * Your selection function(s). You are given the population (and other * information that may be relevant) in 'context', and an array of integers * in 'selections'. You should choose some members of the population that * you deem fit for further genetic processing, and store their indices * in the selections array. You also need to set the variable 'nselected' * to contain the number of selections you have made. This is a pointer * to an integer, so you set it like *nselected = 5. * * The selections array will contain context->nselections integers. This * is set to context->population_size in the main program, but this is done * before the selection initialization function is called, so it is possible * to arrange (in advance) for more space for selections. By doing this, * you might make the job of programming scaling selectors simpler. */ INT selection_function_1(selections, nselected, context) INT *selections; INT *nselected; CONTEXT *context; { } #endif #if defined(APP_HAS_ITS_OWN_MUTATION_METHODS) /* * Define your application's mutation function(s) here. * * The definition of your mutation function(s) will normally be done in * three steps. Firstly you must (unless the function is present in * this file before this comment), forward declare your function(s). * They must all return VOID. Then you set up an array of type * FUNCTION which contains a line for each of the mutation functions * you have written. The example code below should be replaced with * the actual names of your function(s). Lastly, you have to write * the function(s). */ VOID mutation_function_1(); /* * Your mutation function(s). Each line in the array * variable below should contain three things: * * (1) The name of your function that you would like to use on * the command line after the -mutation_func option. * (2) The name of the function as it appears above and below * (where you will write the actual function) * (3) The address of an initialization function. This may be empty, * if not, it will be called before the above function is ever * called and it will be passed the run time context. * (4) A brief description of the behavior of this function * for display with the -functions option. * * Do NOT remove the last line, it marks the end of your mutation * function(s). */ FUNCTION app_mutation_functions[] = { { "cmd_line_name", mutation_function_1, init_function_1, "description" }, { (STRING) 0, (PFPV) 0, (PFV) 0, (STRING) 0 } }; /* * Mutate the first 'n' members of context->population. The mutation * probability is in context->mutation_prob. You can access this * using the DO_MUTATE macro defined in public.h (which is included * by types.h). Use it in an if statement such as * * if (DO_MUTATE) { ... }. * * Faster would be to use the probability to calculate a random * variable from a Poisson distribution that tells you when the * next muattion will occur. Then all you do is wait until that many * loci have passed, do a mutation, calculate the next mutation time * and so on. */ INT mutation_function_1(n, context) INT n; CONTEXT *context; { } #endif #if defined(APP_WANTS_TO_DUMP) /* * When (if) this function gets called, an interrupt has been received by * the main program. You should write out information pertinent to this * run that will be needed when the run is resumed. This should be in a * format that is easily read back in by app_restore_state(). Look at * dump_state() in main.c if you want an example of how to do this. * busy_beavers/busy_beavers.c contains a simpler example. */ VOID app_dump_state(fp, context) FILE *fp; CONTEXT *context; { } /* * This function will be called when the main program is trying to * restart a run that was interrupted. Read from 'fp' exactly the * information you wrote to it with app_dump_state(). No more and no less, * otherwise the main program will get confused and your application's * run will not be restored properly. Look at restore_state() in main.c * if you want an example of how to do this. busy_beavers/busy_beavers.c * contains a simpler example. */ VOID app_restore_state(fp, context) FILE *fp; CONTEXT *context; { } #endif #if defined(APP_MAY_TAKE_COMMAND_LINE_ARGUMENTS) /* * This function will be called if you are interested in command line * arguments following the options. You might, for instance, want to * have the user give some file names that you will interpret here in * a fixed way. Argc and argv are just as they would be for a main * program, but argv[0] does not contain the program name but your * first option. Similarly, argc contains the number of arguments left * to process. Hence, argv[0],... , argv[argc - 1] are valid arguments. */ VOID app_command_line_arguments(argc, argv) INT argc; STRING *argv; { } #endif #if ! defined(APP_INDIVIDUALS_ARE_STRINGS) /* * For the purposes of sorting by genome, the main program needs to know * how to compare two genomes. If genomes are strings, it will use strcmp, * but your genome is something else. If you can think of a meaningful * way to rank two genomes, use it here. Return 0 if the genomes are * equal, something greater than 0 if a > b and something less than 0 * if a < b. * * If you are not interested in sorting by genome ever, just return 0. * Then when individuals are compared by fitness and then by genome, * things will probably work. */ INT app_compare_genomes(a, b) INDIVIDUAL *a; INDIVIDUAL *b; { } #endif #if defined(APP_DUMPS_INDIVIDUALS) VOID app_dump_individual(fp, individual, context); FILE *fp; INDIVIDUAL *individual; CONTEXT *context; { } VOID app_restore_individual(fp, individual, context) FILE *fp; INDIVIDUAL *individual; CONTEXT *context; { } #endif