source: branches/AlphaChannel/AI Template/Project/Lib/AddressPriorityQueue.cs

Last change on this file was 191, checked in by chronos, 5 years ago
  • Added: AI Template directory with manual for development of custom AI from original game.
File size: 9.4 KB
Line 
1using System;
2using System.Collections.Generic;
3
4// collection of non-negative integers (called addresses) with non-negative priorities (called distances)
5// based on binary heap
6
7// main operations:
8// * Offer: offer distance to an address
9// * TakeClosest: get and remove address with shortest offered distance
10
11// only the shortest of multiple offered distances to a specific address is kept
12// user must ensure not to offer a distance shorter than those of addresses already taken
13// (easily achieved by the typical usage of this class)
14
15// memory consumption: 4 bytes per potential address + 8 bytes per address in collection
16// time consumption: O(log n) for both main operations, where n is the current size of the collection
17
18namespace Common
19{
20 /// <summary>
21 /// Priority queue on bounded integer address space.
22 /// </summary>
23 class AddressPriorityQueue
24 {
25 /// <summary>
26 /// Creates an empty collection.
27 /// </summary>
28 /// <param name="maxAddress">maximum address allowed</param>
29 public AddressPriorityQueue(int maxAddress)
30 {
31 if (maxAddress < 0 || maxAddress > 0x7FFFFFFE)
32 throw new Exception("AddressPriorityQueue: Maximum capacity is 1<<31 addresses!");
33 this.maxAddress = maxAddress;
34 allocatedSize = 4 * (int)Math.Sqrt(maxAddress + 1); // good for addresses in a 2D-space
35 binaryHeap = new HeapItem[allocatedSize];
36 binaryHeapSize = 0;
37 states = new uint[maxAddress + 1];
38 lastTakenDistance = 0;
39 taken = 0;
40 }
41
42 /// <summary>
43 /// Offer address with distance. Might have been offered with same or different distance before.
44 /// </summary>
45 /// <param name="address">the address</param>
46 /// <param name="distance">the distance</param>
47 /// <returns>true if address was new or offer was better than all former offers for this address</returns>
48 public bool Offer(int address, int distance)
49 {
50 if (address < 0 || address > maxAddress)
51 throw new Exception(string.Format("AddressPriorityQueue.Offer: Address '{0}' is not in valid range!", address));
52 uint state = states[address];
53 if ((state & wasTakenFlag) != 0)
54 {
55 if (state == (Disallowed | wasTakenFlag))
56 throw new Exception(string.Format("AddressPriorityQueue.Offer: Ambiguous usage! Address '{0}' was both offered and disallowed!", address));
57 else
58 return false; // if class is used correctly, the new offer must be worse than one before at this point
59 }
60 else
61 {
62 if (distance < 0 || distance >= Unknown)
63 throw new Exception(string.Format("AddressPriorityQueue.Offer: Distance '{0}' is not in valid range!", distance));
64 int index = (int)state - 1;
65 bool isShorter = true;
66 if (state > 0) // means address is already contained in binary heap
67 isShorter = (distance < binaryHeap[index].distance);
68 if (isShorter)
69 {
70 if (distance < lastTakenDistance)
71 throw new Exception(string.Format("AddressPriorityQueue.Offer: Illegal order of operations! Can't offer distances shorter than those already taken. Last TakeClosest: '{0}' - this Offer: '{1}'.", lastTakenDistance, distance));
72
73 #region binary heap grow algo
74 if (index < 0)
75 { // address is not yet contained in binary heap, insert at bottom
76 index = binaryHeapSize;
77 binaryHeapSize++;
78 if (binaryHeapSize > allocatedSize)
79 {
80 allocatedSize *= 2;
81 if (allocatedSize > maxAddress + 1)
82 allocatedSize = maxAddress + 1;
83 Array.Resize<HeapItem>(ref binaryHeap, allocatedSize);
84 }
85 }
86
87 // correct binary heap, new item can only move up (either shorter distance than before or at bottom)
88 while (index > 0)
89 {
90 int parent = (index - 1) / 2;
91 if (distance >= binaryHeap[parent].distance)
92 break;
93 binaryHeap[index] = binaryHeap[parent];
94 states[binaryHeap[index].address] = (uint)index + 1;
95 index = parent;
96 }
97 binaryHeap[index].distance = distance;
98 binaryHeap[index].address = address;
99 states[binaryHeap[index].address] = (uint)index + 1;
100 #endregion
101
102 return true;
103 }
104 else
105 return false;
106 }
107 }
108
109 /// <summary>
110 /// Does nothing but returns same value as Offer(...) would.
111 /// </summary>
112 /// <param name="address">the address</param>
113 /// <param name="distance">the distance</param>
114 /// <returns>true if address was new or offer was better than all former offers for this address</returns>
115 public bool TestOffer(int address, int distance)
116 {
117 if (address < 0 || address > maxAddress)
118 throw new Exception(string.Format("AddressPriorityQueue.TestOffer: Address '{0}' is not in valid range!", address));
119 uint state = states[address];
120 if ((state & wasTakenFlag) != 0)
121 {
122 if (state == (Disallowed | wasTakenFlag))
123 throw new Exception(string.Format("AddressPriorityQueue.TestOffer: Ambiguous usage! Address '{0}' was both offered and disallowed!", address));
124 else
125 return false;
126 }
127 else
128 {
129 if (distance < 0 || distance >= Unknown)
130 throw new Exception(string.Format("AddressPriorityQueue.TestOffer: Distance '{0}' is not in valid range!", distance));
131 if (state > 0) // means address is already contained in binary heap
132 return distance < binaryHeap[state - 1].distance;
133 else
134 return true;
135 }
136 }
137
138 /// <summary>
139 /// Marks address as disallowed, which changes the Distance(address) query result and forbids Offer calls for this address.
140 /// </summary>
141 /// <param name="address">the address</param>
142 public void Disallow(int address)
143 {
144 if (address < 0 || address > maxAddress)
145 throw new Exception(string.Format("AddressPriorityQueue.Disallow: Address '{0}' is not in valid range!", address));
146 uint state = states[address];
147 if (state != (Disallowed | wasTakenFlag))
148 {
149 if (state != 0)
150 throw new Exception(string.Format("AddressPriorityQueue.Disallow: Ambiguous usage! Address '{0}' was both offered and disallowed!", address));
151 states[address] = (Disallowed | wasTakenFlag);
152 }
153 }
154
155 /// <summary>
156 /// Get and remove address with shortest offered distance.
157 /// </summary>
158 /// <param name="address">the address</param>
159 /// <param name="distance">the distance</param>
160 /// <returns>true if operation was successful, false for empty collection</returns>
161 public bool TakeClosest(out int address, out int distance)
162 {
163 if (binaryHeapSize == 0)
164 {
165 address = 0;
166 distance = 0;
167 return false;
168 }
169
170 address = binaryHeap[0].address;
171 distance = binaryHeap[0].distance;
172 states[address] = (uint)distance | wasTakenFlag;
173 lastTakenDistance = distance;
174 taken++;
175
176 #region binary heap shrink algo
177 binaryHeapSize--;
178 if (binaryHeapSize > 0)
179 {
180 HeapItem last = binaryHeap[binaryHeapSize];
181 int index = 0;
182 int child = 1;
183 while (child < binaryHeapSize)
184 {
185 if (child < binaryHeapSize - 1 && binaryHeap[child].distance > binaryHeap[child + 1].distance)
186 child++; // right child instead of left
187 if (last.distance <= binaryHeap[child].distance)
188 break;
189
190 binaryHeap[index] = binaryHeap[child];
191 states[binaryHeap[index].address] = (uint)index + 1;
192 index = child;
193 child = 2 * child + 1;
194 }
195 binaryHeap[index] = last;
196 states[last.address] = (uint)index + 1;
197 }
198 #endregion
199
200 return true;
201 }
202
203 /// <summary>
204 /// Remove all addresses from collection and delete calculations. Collection returns to state as after
205 /// new except that possibly resized memory is kept.
206 /// </summary>
207 public void Clear()
208 {
209 if (binaryHeapSize + taken > 0)
210 {
211 binaryHeapSize = 0;
212 Array.Clear(states, 0, maxAddress + 1);
213 lastTakenDistance = 0;
214 taken = 0;
215 }
216 }
217
218 /// <summary>
219 /// Status tracker. Returns shortest distance to an address, if that address was already taken.
220 /// Otherwise, returns Unknown.
221 /// If address was disallowed, returns Disallowed.
222 /// </summary>
223 /// <param name="address">address</param>
224 /// <returns>shortest distance to address</returns>
225 public int Distance(int address)
226 {
227 uint state = states[address];
228 if ((state & wasTakenFlag) != 0)
229 return (int)(state & stateDataMask);
230 else
231 return Unknown;
232 }
233 public const int Disallowed = 0x7FFFFFFF;
234 public const int Unknown = 0x7FFFFFFE;
235
236 /// <summary>
237 /// Number of different addresses offered.
238 /// </summary>
239 public int Count { get { return binaryHeapSize + taken; } }
240
241 /// <summary>
242 /// Number of addresses taken from the collection using TakeClosest.
243 /// </summary>
244 public int Taken { get { return taken; } }
245
246 #region private stuff
247 protected const uint wasTakenFlag = 0x80000000;
248 protected const uint stateDataMask = 0x7FFFFFFF;
249
250 protected struct HeapItem
251 {
252 public int address;
253 public int distance;
254 }
255
256 protected HeapItem[] binaryHeap;
257 protected int binaryHeapSize;
258
259 // multiple funtion memory with address as index:
260 // wasTakenFlag indicates that address should not be taken anymore,
261 // flag is set after address was returned by TakeClosest or disallowed
262 // if wasTakenFlag is not set, lower 31 bits contain index in binaryHeap + 1 (0 meaning not contained)
263 // if wasTakenFlag is set, lower 31 bits contain shortest distance resp. Disallowed
264 protected uint[] states;
265
266 protected int maxAddress;
267 protected int allocatedSize;
268 protected int lastTakenDistance;
269 protected int taken;
270 #endregion
271 }
272}
Note: See TracBrowser for help on using the repository browser.