Oolite 1.91.0.7604-240417-a536cbe
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 NSEnumerator *roleEnum = nil;
119 NSString *role = nil;
120 float probability;
121 NSMutableString *result = nil;
122 BOOL first = YES;
123
124 if (_roleString == nil)
125 {
126 // Construct role string. We always do this so that it's in a normalized form.
127 result = [NSMutableString string];
128 roles = [self sortedRoles];
129 for (roleEnum = [roles objectEnumerator]; (role = [roleEnum nextObject]); )
130 {
131 if (!first) [result appendString:@" "];
132 else first = NO;
133
134 [result appendString:role];
135
136 probability = [self probabilityForRole:role];
137 if (probability != 1.0f)
138 {
139 [result appendFormat:@"(%g)", probability];
140 }
141 }
142
143 _roleString = [result copy];
144 }
145
146 return _roleString;
147}
148
149
150- (BOOL)hasRole:(NSString *)role
151{
152 return role != nil && [_rolesAndProbabilities objectForKey:role] != nil;
153}
154
155
156- (float)probabilityForRole:(NSString *)role
157{
158 return [_rolesAndProbabilities oo_floatForKey:role defaultValue:0.0f];
159}
160
161
162- (BOOL)intersectsSet:(id)set
163{
164 if ([set isKindOfClass:[OORoleSet class]]) set = [set roles];
165 else if (![set isKindOfClass:[NSSet class]]) return NO;
166
167 return [[self roles] intersectsSet:set];
168}
169
170
171- (NSSet *)roles
172{
173 if (_roles == nil)
174 {
175 _roles = [[NSSet alloc] initWithArray:[_rolesAndProbabilities allKeys]];
176 }
177 return _roles;
178}
179
180
181- (NSArray *)sortedRoles
182{
183 return [[_rolesAndProbabilities allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
184}
185
186
187- (NSDictionary *)rolesAndProbabilities
188{
189 return _rolesAndProbabilities;
190}
191
192
193- (NSString *)anyRole
194{
195 NSEnumerator *roleEnum = nil;
196 NSString *role = nil;
197 float prob, selected;
198
199 selected = randf() * _totalProb;
200 prob = 0.0f;
201
202 if ([_rolesAndProbabilities count] == 0) return nil;
203
204 for (roleEnum = [_rolesAndProbabilities keyEnumerator]; (role = [roleEnum nextObject]); )
205 {
206 prob += [_rolesAndProbabilities oo_floatForKey:role];
207 if (selected <= prob) break;
208 }
209 if (role == nil)
210 {
211 role = [[self roles] anyObject];
212 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);
213 }
214 return role;
215}
216
217
218- (id)roleSetWithAddedRoleIfNotSet:(NSString *)role probability:(float)probability
219{
220 NSMutableDictionary *dict = nil;
221
222 if (role == nil || probability < 0 || ([self hasRole:role] && [self probabilityForRole:role] == probability))
223 {
224 return [[self copy] autorelease];
225 }
226
227 dict = [[_rolesAndProbabilities mutableCopy] autorelease];
228 [dict setObject:[NSNumber numberWithFloat:probability] forKey:role];
229 return [[[[self class] alloc] initWithRolesAndProbabilities:dict] autorelease];
230}
231
232
233- (id)roleSetWithAddedRole:(NSString *)role probability:(float)probability
234{
235 NSMutableDictionary *dict = nil;
236
237 if (role == nil || probability < 0 || [self hasRole:role])
238 {
239 return [[self copy] autorelease];
240 }
241
242 dict = [[_rolesAndProbabilities mutableCopy] autorelease];
243 [dict setObject:[NSNumber numberWithFloat:probability] forKey:role];
244 return [[[[self class] alloc] initWithRolesAndProbabilities:dict] autorelease];
245}
246
247
248- (id)roleSetWithRemovedRole:(NSString *)role
249{
250 NSMutableDictionary *dict = nil;
251
252 if (![self hasRole:role]) return [[self copy] autorelease];
253
254 dict = [[_rolesAndProbabilities mutableCopy] autorelease];
255 [dict removeObjectForKey:role];
256 return [[[[self class] alloc] initWithRolesAndProbabilities:dict] autorelease];
257}
258
259@end
260
261
262@implementation OORoleSet (OOPrivate)
263
264- (id)initWithRolesAndProbabilities:(NSDictionary *)dict
265{
266 NSEnumerator *roleEnum = nil;
267 NSString *role = nil;
268 float prob;
269
270 if (dict == nil)
271 {
272 [self release];
273 return nil;
274 }
275
276 self = [super init];
277 if (self == nil) return nil;
278
279 // Note: _roles and _roleString are derived on the fly as needed.
280 // MKW 20090815 - if we are re-initialising this OORoleSet object, we need
281 // to ensure that _roles and _roleString are cleared.
282 // Why would we be re-initing? That's never valid. -- Ahruman 2010-02-06
283 assert(_roles == nil && _roleString == nil);
284
285 NSMutableDictionary *tDict = [[dict mutableCopy] autorelease];
286 float thargProb = [dict oo_floatForKey:@"thargon" defaultValue:0.0f];
287
288 if ( thargProb > 0.0f && [dict objectForKey:@"EQ_THARGON"] == nil)
289 {
290 [tDict setObject:[NSNumber numberWithFloat:thargProb] forKey:@"EQ_THARGON"];
291 [tDict removeObjectForKey:@"thargon"];
292 }
293
294 _rolesAndProbabilities = [tDict copy];
295
296 for (roleEnum = [dict keyEnumerator]; (role = [roleEnum nextObject]); )
297 {
298 prob = [dict oo_floatForKey:role defaultValue:-1];
299 if (prob < 0)
300 {
301 OOLog(@"roleSet.badValue", @"Attempt to create a role set with negative or non-numerical probability for role %@.", role);
302 [self release];
303 return nil;
304 }
305
306 _totalProb += prob;
307 }
308
309 return self;
310}
311
312@end
313
314
315NSDictionary *OOParseRolesFromString(NSString *string)
316{
317 NSMutableDictionary *result = nil;
318 NSArray *tokens = nil;
319 NSUInteger i, count;
320 NSString *role = nil;
321 float probability;
322 NSScanner *scanner = nil;
323
324 // Split string at spaces, sanity checks, set-up.
325 if (string == nil) return nil;
326
327 tokens = ScanTokensFromString(string);
328 count = [tokens count];
329 if (count == 0) return nil;
330
331 result = [NSMutableDictionary dictionaryWithCapacity:count];
332
333 // Scan tokens, looking for probabilities.
334 for (i = 0; i != count; ++i)
335 {
336 role = [tokens objectAtIndex:i];
337
338 probability = 1.0f;
339 if ([role rangeOfString:@"("].location != NSNotFound)
340 {
341 scanner = [[NSScanner alloc] initWithString:role];
342 [scanner scanUpToString:@"(" intoString:&role];
343 [scanner scanString:@"(" intoString:NULL];
344 if (![scanner scanFloat:&probability]) probability = 1.0f;
345 // Ignore rest of string
346
347 [scanner release];
348 }
349
350 // shipKey roles start with [ so other roles can't
351 if (0 <= probability && ![role hasPrefix:@"["])
352 {
353 [result setObject:[NSNumber numberWithFloat:probability] forKey:role];
354 }
355 }
356
357 if ([result count] == 0) result = nil;
358 return result;
359}
#define OOLog(class, format,...)
Definition OOLogging.h:88
return self
unsigned count
return nil
NSDictionary * OOParseRolesFromString(NSString *string)
NSDictionary * OOParseRolesFromString(NSString *string)
Definition OORoleSet.m:315
NSMutableArray * ScanTokensFromString(NSString *values)
float randf(void)