Abstract: Corewar is a programming game played on a board simulating computer memory. This board is called the core, and consists of N cells, each containing an assembly language command. Programs run simultaneously, with the objective of killing other programs in core and thus becoming the sole owner of the core. Addition, subtraction, and multiplication give the core a Z/NZ ring structure. N is called the coresize. Popular values of N are 8000, 8192, and 55440. In this article, we examine ways to optimize simple bombing or scanning step sizes. We give formulas to measure 'optimization' of a step for a given coresize, and prove that imp-pairs have the same optima score. Contents: (0) Terminology (1) Euclid's Algorithm and Continued Fractions (2) Some Models for Bombing Core (incomplete) (3) A Better Formula for the Length of Intervals (4) The Optima Step Formula (5) The Find-X Formula (omitted in current draft) (6) Locally Fibonacci Step-Sizes (omitted in current draft) (7) Calculations (omitted in current draft) --- (0) Terminology We denote subscripts and superscripts in a TeX-ish way. So "X sub 1" will be written X_1, and X^2 = X*X. If we need to perform math on subscripts, we will use curly braces to denote groupings. We also use the following symbols: Z+ : The set of all positive integers. (A,B) : The greatest common factor of positive integers A,B. [x] : The integral part of a non-negative real number x. A|B : The integer A divides the integer B evenly (that is, without remainder.) --- (1) Euclid's Algorithm and Continued Fractions In this section, we build the mathematical framework for the results of this article. We explore the relationship betwwen Euclid's Algorithm and continued fractions, and prove a classic result of Euler, who seems to have anticipated Corewar in 1764. Recall that Euclid's Algorithm computes (A,B), with integers A>B>0, as follows: Let X_0 = A and X_1 = B. Inductively define X_{i+1} = X_{i-1} - X_i*Y_i, (1) with Y_i=[X_{i-1}/X_i]. (2) Clearly, 0 <= X_{i+1} < X_i. If X_{i+1}=0, our induction must stop (because Y_{i+1} is undefined). In this case, define k to be the largest i for which X_i is non-zero. Since X_{i+1}0. Hence, X_k and (A,B) have the same factors, so X_k=(A,B). Definition: Given A_1,A_2,...,A_n in Z+, we define the continued fraction generated by the A_i's as = A_1+1/(A_2+1/...(A_{n-1}+1/A_n)...) (3) We define the length of to be n. Clearly, is always rational. We define the numerator of to be p, where p/q is the reduced fraction with p/q= and denote it by p=[A_1,...,A_n]. We allow "empty" numerators, defining []=1. Propositon 1: Given A,B in Z+ with A>B, we have A/B = where the Y_i's and k are defined by Euclid's algorithm using (1) and (2). Proof: We the notations of Euclid's algorithm above and induction on k. When k=1, we have X_2=0 (by definition of k), so we know by (1) with i=1 that 0 = X_0-Y_1*X_1. Hence, = Y_1 = X_0/X_1. Now, suppose k>1. Note that X_1>X_2>0, so we can use Euclid's algorthm on A'= X_1, B'= X_2 to find each Y'_i, and it is easy to see that Y'_i = Y_{i+1}. Hence, for induction we may assume that X_1 / X_2 = . We then have = Y_1+1/ = Y_1+1/(X_1/X_2) = (Y_1 * X_1 + X_2)/X_1 = X_0/X_1 = A/B. [box] Corollary 2: If we are given the Y_i's and m=(A,B), we can recover A and B. Proof: is rational, hence can be uniquely written as a reduced fraction a/b. Existence: Since this fraction is reduced, we have (a,b)=1. Define A= ma, B= mb. Then A/B=a/b= and (A,B)=m*(a,b)=1. Uniqueness: If A/B=A'/B'= and (A,B)=(A',B')=m then A/m,B/m are in Z+ and (A/m,B/m)=1, so (A/m)/(B/m) is the reduced fraction a/b. Hence A/m=a, B/m=b. Similary, A'/m=a, B'/m=b. Thus A=A' and B=B'. [box] Corollary 3: Given A,B in Z+ with A>B and (A,B)=1, we have A = [Y_1,...,Y_k], B = [Y_2,...,Y_k] where the Y_i's and k are defined by Euclid's algorithm using (1) and (2). Proof: From Proposition 1, we know A/B=. This fraction is reduced when (A,B)=1, so A = [Y_1,...,Y_k] by definition. If k=1, then B=1=[]. Now if k>1 and C/D is the reduced fraction , then we have A/B= Y_1 + 1/(C/D) = (Y_1*C + D)/C. The fractions on both sides of this equation are reduced, hence B=C. But C = [Y_2,...,Y_k] by definition. [box] Corollary 4: For any continued fraction with n>=2, the following identity holds: [A_1,...,A_n] = A_1*[A_2,...,A_n] + [A_3,...,A_n]. (4) Proof: If A/B= and C/D= are both reduced fractions, then A/B= (A_1*C + D)/C, as in the proof of Corollary 3. Multiplying both sides by B=C, we obtain: A = A_1*C + D. From Corollary 3, we have A = [A_1,...,A_n], C = [A_2,...,A_n], and D = [A_3,...,A_n]. [box] Examples: 43/10 = <4,3,3>. Also, 43/10 = <4,3,2,1>, even though these are not the Y_i defined in (2). 43/30 = <1,2,3,4>. If (A,B)=1 and A/B = <1,1,...,1> (k times), then A=F_{k+1} and B=F_k, where F_i is the i'th term in the Fibonacci sequence {1,1,2,3,5,8,13,...}. We now come to the main result of this section, proved by Euler in 1764. Theorem 5: For any continued fraction , the following relations hold: [A_1,...,A_n]=[A_n,...,A_1] (5) [A_2,...,A_n] * [A_1,...,A_{n-1}] = [A_1,...,A_n] * [A_2,...,A_{n-1}] - (-1)^n (n>1) (6) Proof: We use induction on n. When n=0 or n=1, (5) is tautological. And for n=2, we have, by (4), [A_1,A_2]= A_1 * [A_2]+ [] = A_1 * A_2 + 1. Hence, [A_2] * [A_1] = A_2 * A_1 = (A_1 * A_2 + 1)*1 -1 = [A_1,A_2]*[]-(-1)^2, so (6) holds. For n>=2, we use (4) and induction on (5) repeatedly to get: [A_1,...,A_2] = A_1 * [A_2,...,A_n] + [A_3,...,A_n] = A_1 * [A_n,...,A_2] + [A_n,...,A_3] = A_1 * (A_n * [A_{n-1},...,A_2] + [A_{n-2},...,A_2]) + A_n * [A_{n-1},...,A_3] + [A_{n-2},...,A_3] = A_1 * A_n * [A_2,...,A_{n-1}] + A_1 * [A_2,...,A_{n-2}] + A_n * [A_3,...,A_{n-1}] + [A_3,...,A_{n-2}]. If we replace (A_1,...,A_n) with (A_n,...,A_1), the first and fourth terms of the right-hand expression stay the same, and the second and third terms get swapped. Thus, [A_1,...,A_n] = [A_n,...,A_1] since they both equal the same expression. This proves (5) for all n>0. Finally, for n>=3, we can assume by induction that (6) holds for . In other words, [A_2,...,A_{n-1}]*[A_3,...,A_n}] = [A_3,...,A_{n-1}]*[A_2,...,A_n] -(-1)^{n-1}. Thus, [A_1,...,A_n]*[A_2,...,A_{n-1}] = (A_1*[A_2,...,A_n]+[A_3,...,A_n]) *[A_2,...,A_{n-1}] = A_1*[A_2,...,A_n]*[A_2,...,A_{n-1}] +[A_3,...,A_n]*[A_2,...,A_{n-1}] = A_1*[A_2,...,A_n]*[A_2,...,A_{n-1}] +[A_3,...,A_{n-1}]*[A_2,...,A_n]+(-1)^n = [A_2,...,A_n]*(A_1*[A_2,...,A_{n-1}]+[A_3,...,A_{n-1}])+(-1)^n = [A_2,...,A_n]*[A_1,...,A_{n-1}]+(-1)^n. This is (6) for , so by induction, (6) holds for all n>1. [box] Corollary 6: Given positive integers A,B, with A/B = , define positive integers A',B' by A'/B' = (7) where (A',B')=(A,B) (8) Then the following relationships hold: A' = A; (9) B'*B = -(-1)^k*(A,B)^2 (mod A). (10) Proof: Let m=(A,B). Then (A/m)/(B/m) is the reduced form of A/B, whence A = m*[Y_1,...,Y_k], B = m*[Y_2,...,Y_k]. Similarly, A' = m*[Y_k,...,Y_1], B' = m*[Y_{k-1},...,Y_1]. Then (9) follows from (5), and (6) can be written as: (B/m)*(B'/m)=(A/m)*[Y_2,...,Y_{k-1}]-(-1)^k. We multiply both sides by m^2 to get B*B'=A*m*[Y_2,...,Y_{k-1}]-(-1)^k*m^2. Then (10) follows from passing to congruence (mod A). [box] --- (2) Some Models for Bombing Core Let N be the coresize and let KZ/NZ given by f(n)=n+N. Rather than mess with cosets, we will refer to elements of core by representatives in Z. For a0, let U_j be the interval containing 0 in the partition after j bombs. Theorem 7: For all j with 0=0 is in the partition. In the first case, we have ||[a,b)||=||[d+g*K,e+G*K)||=e-d=||[d,e)||, so we are done. Otherwise, since d is in the bombing run, we know that d=l*K for some l with 1. Applying Corollary 3 to X_0/m,X_1/m, we get X_0= m*[Y_1,...,Y_k] X_1= m*[Y_2,...,Y_k] and inductively using Corollary 4 with equation (1), we get X_i= m*[Y_{i+1},...,Y_k] (0<=i<=k). (12) Inductively using Corollary 4 with equation (11), we also get Z_i= [Y_{i-1},...,Y_1] (1<=i<=k+1). (13) Now we can describe the bombing sequence as a series of bombing runs by looking at the intervals U_i containing 0. Definition: The i'th bombing run for i=1 and X_{i-1}-p*X_i>= 0, whch means p<=Y_i. We have by hypothesis that the first bomb in the i'th bombing run is dropped at X_{i-1}-X_i and shrinks U_j. So, what is the gap in time between a bombs being dropped at X_{i-1}-(p-1)*X_i and X_{i-1}-p*X_i? Since all bombs are evenly spaced, this gap will be the same for all p, and with p=1, we know that the Z_{i-1}'th bomb dropped at X_{i-1} and the Z_{i-1}+Z_i'th bomb drops at X_{i-1}-X_i. Thus the value of p increases every Z_i bombs. We know that the value of p starts at one with the Z_i+Z_{i-1}'th bomb, so we can verify that the p given in the statement of the theorem is correct. Next, we must verify that the bombs we just dropped were the i'th bombing run. We started with bomb Z_i+Z_{i-1}, and dropped Z_i bombs for each p between 1 and Y_i (except for the last bombing run, where p only ranged between 1 and Y_i-1). Thus, the last bomb in this bombing was the Z_i+Z_{i-1}+Y_i*Z_i-1=Z_i+Z_{i+1}-1'th bomb (or Z_{k+1}-1=N/m-1 for the last run). From the last two paragraphs, with p=Y_i+1, we see that the first bomb of the next bombing run is dropped at X_{i-1}-(p+1)*X_i= X_{i+1}-X_i. And with p=Y_i, we have the Z_{i+1}'th bomb is the Z_i*Y_i+Z_{i-1}'th bomb, which drops at X_{i-1}-X_i*Y_i= X_{i+1}. This finishes the induction step. [box] --- (4) The Optima Step Formula In Corewar, the step size of a bombing or scanning pattern is the distance between successive bombing or scanning locations. Chosing a good step size is critical to the success of a warrior, so we need a way of measuring the effectiveness of a step size. What makes one step size better than another? Here is one train of thought: Larger enemies can usually be found faster than small ones. If your step takes the same time to find small enemies as large ones, it probably needs to be optimized against larger enemies. On the other hand, if your step size is too coarse, small programs can slip through the cracks. For instance, assuming 8000 instructions in core, a step size of 4000 is bad. The first two bombs are spaced well, but the pattern only hits two core locations. An ideal pattern will divide the core into successively smaller chunks, until it has checked all locations in core (except where your code resides). We can measure the success of this method by adding the lengths of the largest untouched segments during different stages of bombing. This sum is the optima function of a coresize and step. A lower function value between step-sizes with the same greatest common factor indicates a more optimal pattern. Definition: The optima function is defined to be: O(N,K)= sum_j( ||U_j|| ). Corollary 8 assures that this is the sum we want. Proposition 10: O(N,K) = O(N,N-K). Proof: In coresize N, we can consider a step-size of N-K as a step-size of K bombing in the other direction. If the U_j are the intervals containing 0 for step-size K, as above, then, by symmetry, the intervals U'_j containing 0 for step-size N-K will just be the U_j reversed; that is, if for some j, U_j=[-a,b), then U'_j=[-b,a). Note that ||U_j|| = a+b = ||U'_j||. We sum over all j to get the result. [box] Theorem 11: O(N,K)= sum_{i=1}^k(X_{i-1}*Z{i+1}-X_i*Z_i-X_i*Z_i*(Y_i*(Y_i-1)/2) Proof: We write O(N,K)= sum_{i=1}^k O_i(N,K), where each O_i is the sum of the ||U_j|| from the i'th bombing run. In the i'th bombing run, we have (by theorem 9) Z_i intervals of length X_{i-1}-(p-1)*X_i, for p ranging from 1 to Y_i. Adding these up, we think that O_i(N,K)= sum_{p=1}^{Y_i} Z_i*(X_{i-1}-(p-1)*X_i) = sum_{p=1}^{Y_i}(Z_i*X_{i-1}) - sum_{p=1}^{Y_i}(Z_i*X_i*(p-1)) = Y_i*Z_i*X_{i-1} - Z_i*X_i*(Y_i*(Y_i-1)/2) Well, not quite. We forgot that the last bombing run only uses the values of p from 1 to Y_k-1. Thus we must add the correction term -Z_k*(X_{k-1}-(Y_k-1)*X_k)= -Z_k*(X_{k-1}-Y_k*X_k+X_k) = -Z_k*(X_{k+1}+X_k) = -Z_k*X_k since X_{k+1}=0. We thus have O(N,K)= (15) sum_{i=1}^k( Y_i*Z_i*X_{i-1} - Z_i*X_i*(Y_i*(Y_i-1)/2) ) -Z_k*X_k. We also have the following telescoping sum: sum_{i=1}^k( X_i*Z_i - X_{i-1}*Z_{i-1}) = X_k*Z_k - X_0*Z_0 = X_k*Z_k since Z_0=0. Substituting this into (15), we get O(N,K)= (16) sum_{i=1}^k( Y_i*Z_i*X_{i-1} - Z_i*X_i*(Y_i*(Y_i-1)/2) - X_i*Z_i + X_{i-1}*Z_{i-1}). and the first and fourth terms of the sum combine thusly: Y_i*Z_i*X_{i-1} + X_{i-1}*Z_{i-1} = X_{i-1}*(Y_i*Z_i+Z_{i-1}) = X_{i-1}*Z{i+1}. to yield the formula we wished to prove. [box] Corollary 12: If {K,K''} is an imp-pair for coresize N, then O(N,K)=O(N,K''). Proof: Recall that {K,K''} is an imp-pair if and only if K*K'' = 1 (mod N). For any given K,N, we know that K'' exists if and only if (K,N)=1, and we know that K'' is unique. Construct K' and N' as in corollary 6. Then we have N=[Y_1,...,Y_k], K=[Y_2,...,Y_k], N'=[Y_k,...,Y_1], K'=[Y_{k-1},...,Y_1]. If we define X'_i,Y'_i, and Z'_i by X'_0=N',X'_1=K' and the recurrence relations in section 3, we get the follwing for all applicable i: Y'_i=Y_{k+1-i} X'_i=Z_{k+1-i} Z'_i=X_{k+1-i}. If we apply theorem 11 and reverse the order of summation, we get O(N',K')= sum_{i=1}^k(X'_{i-1}*Z'{i+1} -X'_i*Z'_i -X'_i*Z'_i*(Y'_i*(Y'_i-1)/2) = sum_{i=1}^k(X'_{k+1-(i-1)}*Z'_{k+1-(i+1)} -X'_{k+1-i}*Z'_{k+1-i} -X'_{k+1-i}*Z'_{k+1-i}*(Y'_{k+1-i}*(Y'_{k+1-i}-1)/2) = sum_{i=1}^k(Z_{i+1}*X_{i-1} -Z_i*X_i -Z_i*X_i*(Y_i*(Y_i-1)/2) = O(N,K). Now, by corollary 6, we have N'=N and K*K'= +/- 1 (mod N), so by uniqueness of K'', we have either K''=K' (mod N) or K''=-K' (mod N). In the first case, O(N,K'')=O(N',K'), and in the second case, O(N,K'')= O(N,N-K')=O(N',K') by proposition 10. [box]