Oolite
Loading...
Searching...
No Matches
OORoleSet.m
Go to the documentation of this file.
1/*
2
3OORoleSet.m
4
5
6Copyright (C) 2007-2013 Jens Ayton
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in all
16copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
25
26*/
27
28#import "OORoleSet.h"
29
30#import "OOStringParsing.h"
32#import "OOLogging.h"
33
34
35@interface OORoleSet (OOPrivate)
36
37- (id)initWithRolesAndProbabilities:(NSDictionary *)dict;
38
39@end
40
41
42@implementation OORoleSet
43
44+ (instancetype) roleSetWithString:(NSString *)roleString
45{
46 return [[[self alloc] initWithRoleString:roleString] autorelease];
47}
48
49
50+ (instancetype) roleSetWithRole:(NSString *)role probability:(float)probability
51{
52 return [[[self alloc] initWithRole:role probability:probability] autorelease];
53}
54
55- (id)initWithRoleString:(NSString *)roleString
56{
57 NSDictionary *dict = nil;
58
59 dict = OOParseRolesFromString(roleString);
60 return [self initWithRolesAndProbabilities:dict];
61}
62
63
64- (id)initWithRole:(NSString *)role probability:(float)probability
65{
66 NSDictionary *dict = nil;
67
68 if (role != nil && 0 <= probability)
69 {
70 dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:probability] forKey:role];
71 }
72 return [self initWithRolesAndProbabilities:dict];
73}
74
75
76- (void)dealloc
77{
78 [_roleString autorelease];
79 [_rolesAndProbabilities autorelease];
80 [_roles autorelease];
81
82 [super dealloc];
83}
84
85
86- (NSString *)description
87{
88 return [NSString stringWithFormat:@"<%@ %p>{%@}", [self class], self, [self roleString]];
89}
90
91
92- (BOOL)isEqual:(id)other
93{
94 if ([other isKindOfClass:[OORoleSet class]])
95 {
96 return [_rolesAndProbabilities isEqual:[other rolesAndProbabilities]];
97 }
98 else return NO;
99}
100
101
102- (NSUInteger)hash
103{
104 return [_rolesAndProbabilities hash];
105}
106
107
108- (id)copyWithZone:(NSZone *)zone
109{
110 // Note: since object is immutable, a copy is no different from the original.
111 return [self retain];
112}
113
114
115- (NSString *)roleString
116{
117 NSArray *roles = nil;
118 NSString *role = nil;
119 float probability;
120 NSMutableString *result = nil;
121 BOOL first = YES;
122
123 if (_roleString == nil)
124 {
125 // Construct role string. We always do this so that it's in a normalized form.
126 result = [NSMutableString string];
127 roles = [self sortedRoles];
128 foreach (role, roles)
129 {
130 if (!first) [result appendString:@" "];
131 else first = NO;
132
133 [result appendString:role];
134
135 probability = [self probabilityForRole:role];
136 if (probability != 1.0f)
137 {
138 [result appendFormat:@"(%g)", probability];
139 }
140 }
141
142 _roleString = [result copy];
143 }
144
145 return _roleString;
146}
147
148
149- (BOOL)hasRole:(NSString *)role
150{
151 return role != nil && [_rolesAndProbabilities objectForKey:role] != nil;
152}
153
154
155- (float)probabilityForRole:(NSString *)role
156{
157 return [_rolesAndProbabilities oo_floatForKey:role defaultValue:0.0f];
158}
159
160
161- (BOOL)intersectsSet:(id)set
162{
163 if ([set isKindOfClass:[OORoleSet class]]) set = [set roles];
164 else if (![set isKindOfClass:[NSSet class]]) return NO;
165
166 return [[self roles] intersectsSet:set];
167}
168
169
170- (NSSet *)roles
171{
172 if (_roles == nil)
173 {
174 _roles = [[NSSet alloc] initWithArray:[_rolesAndProbabilities allKeys]];
175 }
176 return _roles;
177}
178
179
180- (NSArray *)sortedRoles
181{
182 return [[_rolesAndProbabilities allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
183}
184
185
186- (NSDictionary *)rolesAndProbabilities
187{
188 return _rolesAndProbabilities;
189}
190
191
192- (NSString *)anyRole
193{
194 NSString *role = nil;
195 float prob, selected;
196
197 selected = randf() * _totalProb;
198 prob = 0.0f;
199
200 if ([_rolesAndProbabilities count] == 0) return nil;
201
202 foreachkey (role, _rolesAndProbabilities)
203 {
204 prob += [_rolesAndProbabilities oo_floatForKey:role];
205 if (selected <= prob) break;
206 }
207 if (role == nil)
208 {
209 role = [[self roles] anyObject];
210 OOLog(@"roleSet.anyRole.failed", @"Could not get a weighted-random role from role set %@, returning unweighted selection %@. TotalProb: %g, selected: %g, prob at end: %f", self, role, _totalProb, selected, prob);
211 }
212 return role;
213}
214
215
216- (id)roleSetWithAddedRoleIfNotSet:(NSString *)role probability:(float)probability
217{
218 NSMutableDictionary *dict = nil;
219
220 if (role == nil || probability < 0 || ([self hasRole:role] && [self probabilityForRole:role] == probability))
221 {
222 return [[self copy] autorelease];
223 }
224
225 dict = [[_rolesAndProbabilities mutableCopy] autorelease];
226 [dict setObject:[NSNumber numberWithFloat:probability] forKey:role];
227 return [[[[self class] alloc] initWithRolesAndProbabilities:dict] autorelease];
228}
229
230
231- (id)roleSetWithAddedRole:(NSString *)role probability:(float)probability
232{
233 NSMutableDictionary *dict = nil;
234
235 if (role == nil || probability < 0 || [self hasRole:role])
236 {
237 return [[self copy] autorelease];
238 }
239
240 dict = [[_rolesAndProbabilities mutableCopy] autorelease];
241 [dict setObject:[NSNumber numberWithFloat:probability] forKey:role];
242 return [[[[self class] alloc] initWithRolesAndProbabilities:dict] autorelease];
243}
244
245
246- (id)roleSetWithRemovedRole:(NSString *)role
247{
248 NSMutableDictionary *dict = nil;
249
250 if (![self hasRole:role]) return [[self copy] autorelease];
251
252 dict = [[_rolesAndProbabilities mutableCopy] autorelease];
253 [dict removeObjectForKey:role];
254 return [[[[self class] alloc] initWithRolesAndProbabilities:dict] autorelease];
255}
256
257@end
258
259
260@implementation OORoleSet (OOPrivate)
261
262- (id)initWithRolesAndProbabilities:(NSDictionary *)dict
263{
264 NSString *role = nil;
265 float prob;
266
267 if (dict == nil)
268 {
269 [self release];
270 return nil;
271 }
272
273 self = [super init];
274 if (self == nil) return nil;
275
276 // Note: _roles and _roleString are derived on the fly as needed.
277 // MKW 20090815 - if we are re-initialising this OORoleSet object, we need
278 // to ensure that _roles and _roleString are cleared.
279 // Why would we be re-initing? That's never valid. -- Ahruman 2010-02-06
280 assert(_roles == nil && _roleString == nil);
281
282 NSMutableDictionary *tDict = [[dict mutableCopy] autorelease];
283 float thargProb = [dict oo_floatForKey:@"thargon" defaultValue:0.0f];
284
285 if ( thargProb > 0.0f && [dict objectForKey:@"EQ_THARGON"] == nil)
286 {
287 [tDict setObject:[NSNumber numberWithFloat:thargProb] forKey:@"EQ_THARGON"];
288 [tDict removeObjectForKey:@"thargon"];
289 }
290
291 _rolesAndProbabilities = [tDict copy];
292
293 foreachkey (role, dict)
294 {
295 prob = [dict oo_floatForKey:role defaultValue:-1];
296 if (prob < 0)
297 {
298 OOLog(@"roleSet.badValue", @"Attempt to create a role set with negative or non-numerical probability for role %@.", role);
299 [self release];
300 return nil;
301 }
302
303 _totalProb += prob;
304 }
305
306 return self;
307}
308
309@end
310
311
312NSDictionary *OOParseRolesFromString(NSString *string)
313{
314 NSMutableDictionary *result = nil;
315 NSArray *tokens = nil;
316 NSUInteger i, count;
317 NSString *role = nil;
318 float probability;
319 NSScanner *scanner = nil;
320
321 // Split string at spaces, sanity checks, set-up.
322 if (string == nil) return nil;
323
324 tokens = ScanTokensFromString(string);
325 count = [tokens count];
326 if (count == 0) return nil;
327
328 result = [NSMutableDictionary dictionaryWithCapacity:count];
329
330 // Scan tokens, looking for probabilities.
331 for (i = 0; i != count; ++i)
332 {
333 role = [tokens objectAtIndex:i];
334
335 probability = 1.0f;
336 if ([role rangeOfString:@"("].location != NSNotFound)
337 {
338 scanner = [[NSScanner alloc] initWithString:role];
339 [scanner scanUpToString:@"(" intoString:&role];
340 [scanner scanString:@"(" intoString:NULL];
341 if (![scanner scanFloat:&probability]) probability = 1.0f;
342 // Ignore rest of string
343
344 [scanner release];
345 }
346
347 // shipKey roles start with [ so other roles can't
348 if (0 <= probability && ![role hasPrefix:@"["])
349 {
350 [result setObject:[NSNumber numberWithFloat:probability] forKey:role];
351 }
352 }
353
354 if ([result count] == 0) result = nil;
355 return result;
356}
#define foreachkey(VAR, DICT)
Definition OOCocoa.h:353
#define OOLog(class, format,...)
Definition OOLogging.h:88
return self
unsigned count
return nil
NSDictionary * OOParseRolesFromString(NSString *string)
Definition OORoleSet.m:312
NSDictionary * OOParseRolesFromString(NSString *string)
Definition OORoleSet.m:312
NSMutableArray * ScanTokensFromString(NSString *values)
float randf(void)