sfp 0.2.1 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/sfp/sas.rb DELETED
@@ -1,966 +0,0 @@
1
- module Nuri
2
- module Sas
3
- class Task
4
- attr_reader :variables, :mutexes, :operators, :axioms, :init, :goal
5
- attr_reader :global_variable, :global_operators
6
- attr_accessor :sas_plan, :goal_operator_name
7
-
8
- GlobalVariable = 'global'
9
- GlobalOperator = 'globalop'
10
- SometimeVariable = 'sometime'
11
- SometimeOperator = 'sometime'
12
- GoalOperator = 'goal'
13
- GoalVariable = 'goal'
14
-
15
- def initialize(sas_file)
16
- @goal_operator_name = nil
17
-
18
- f = File.new(sas_file, "r")
19
- ### read version
20
- f.gets
21
- @version = f.gets.to_i
22
- f.gets
23
- ### read metrix
24
- f.gets
25
- @metric = f.gets.to_i
26
- f.gets
27
- ### read variables
28
- @variables = []
29
- total_variables = f.gets.to_i
30
- for i in 1..total_variables
31
- variable = Variable.new(:io => f, :index => i-1)
32
- @global_variable = variable if @global_variable.nil? and variable.is_global
33
- @variables << variable
34
- end
35
- ### skip mutex
36
- total_mutex = f.gets.to_i
37
- @mutexes = []
38
- # TODO -- parse mutex
39
- total_mutex = 0
40
- begin line = f.gets; line.chop!; end until line == "begin_state"
41
- ### read state
42
- @init = State.new
43
- for i in 0..(total_variables-1)
44
- @variables[i].init = @init[i] = f.gets.to_i
45
- end
46
- f.gets # end_state
47
- ### read goal
48
- @goal = Goal.new(:io => f, :variables => @variables)
49
- ### read operators
50
- @operators = []
51
- @global_operators = []
52
- total_operators = f.gets.to_i
53
- for i in 1..total_operators
54
- operator = Operator.new(:io => f, :variables => @variables)
55
- @operators << operator if not operator.is_global
56
- @global_operators << operator if operator.is_global
57
- end
58
- ### read axiom
59
- @axioms = []
60
- total_axioms = f.gets.to_i
61
- for i in 1..total_axioms
62
- axiom = Axiom.new(:io => f, :variables => @variables)
63
- @axioms << axiom
64
- end
65
- end
66
-
67
- # Write the planning task to given IO object.
68
- # @param - io: an IO object
69
- def write_io(io)
70
- io.puts('begin_version', @version, 'end_version')
71
- io.puts('begin_metric', @metric, 'end_metric')
72
- io.puts(@variables.length)
73
- @variables.each { |variable| variable.write_io(io) }
74
- io.puts(@mutexes.length)
75
- @mutexes.each { |mutex| mutex.write_io(io) }
76
- @init.write_io(io)
77
- @goal.write_io(io)
78
- io.puts(@operators.length + @global_operators.length)
79
- @operators.each { |operator| operator.write_io(io) }
80
- @global_operators.each { |operator| operator.write_io(io) }
81
- io.puts(@axioms.length)
82
- @axioms.each { |axiom| axiom.write_io(io) }
83
- end
84
-
85
- # Return the partial-order workflow in JSON
86
- # @param - parser: Nuri::Sfp::Parser object
87
- # @return: workflow, initial operators' indexes, length
88
- def get_partial_order_workflow(parser)
89
- plan = self.to_partial_order
90
- return nil, nil, nil if plan.nil? # no-valid plan
91
-
92
- sfw = []
93
- init = []
94
- name_index = {}
95
- distance = plan.length
96
- plan.each_index do |i|
97
- op_name = plan[i].name.split(' ')[0]
98
- operator = parser.operators[op_name]
99
- raise Exception, 'Cannot find operator: ' + op_name if operator.nil?
100
- op_sfw = operator.to_sfw
101
- op_sfw['id'] = i
102
- op_sfw['distance'] = distance
103
- op_sfw['successors'] = []
104
- op_sfw['predecessors'] = []
105
- sfw << op_sfw
106
- name_index[op_name] = i
107
- init << i if plan[i].predecessors.length <= 0
108
- distance -= 1
109
- end
110
- plan.each_index do |i|
111
- plan[i].predecessors.each do |op|
112
- op_name = op.name.split(' ')[0]
113
- j = name_index[op_name]
114
- next if j.nil? # HACK!
115
- sfw[j]['successors'] << i
116
- sfw[i]['predecessors'] << j
117
- end
118
- end
119
- return sfw, init, sfw.length
120
- end
121
-
122
- def goal_state
123
- goal = []
124
- if @goal_operator_name.nil?
125
- @goal.each { |var| goal << {:id => var.index, :value => var.goal} }
126
- else
127
- @operators.each do |op|
128
- if op.name == @goal_operator_name
129
- op.prevails.each { |param|
130
- goal << {:id => param.var.index, :value => param.prevail}
131
- }
132
- break
133
- end
134
- end
135
- end
136
- goal
137
- end
138
-
139
- def final_state
140
- state = @init.clone
141
- self.collect_operators.each { |op| state.apply!(op) }
142
- final = []
143
- #for i in 0..(state.length-1)
144
- # final << {:id => i, :value => state[i]} #if state[i] != @init[i]
145
- #end
146
- for i in 0..(state.length-1)
147
- next if @variables[i].is_global or @variables[i].is_goal
148
- final << {:id => i, :value => state[i]}
149
- end
150
- return final
151
- end
152
-
153
- protected
154
- def self.is_global_variable?(variable_name)
155
- return (variable_name.split('_')[2] == GlobalVariable)
156
- end
157
-
158
- def self.is_global_operator?(operator_name)
159
- return (operator_name.split('-')[1] == GlobalOperator)
160
- end
161
-
162
- def self.is_sometime_operator?(operator_name)
163
- return (operator_name.split('-')[1] == SometimeOperator)
164
- end
165
-
166
- def self.is_sometime_variable?(variable_name)
167
- return (variable_name.split('-')[1] == SometimeVariable)
168
- end
169
-
170
- def self.is_goal_variable?(variable_name)
171
- return (variable_name.split('-')[1] == GoalVariable)
172
- end
173
-
174
- def self.is_goal_operator?(operator_name)
175
- return (operator_name.split('-')[1] == GoalOperator)
176
- end
177
-
178
- def find_operator(name)
179
- @operators.each { |op| return op if op.name == name }
180
- return nil
181
- end
182
-
183
- def find_global_operator(name)
184
- @global_operators.each { |op| return op if op.name == name }
185
- return nil
186
- end
187
-
188
- def to_partial_order
189
- if @global_operators.length <= 0
190
- return self.plan_without_trajectory_to_partial_order
191
- else
192
- return self.plan_with_trajectory_to_partial_order
193
- end
194
- end
195
-
196
- def collect_operators
197
- plan = []
198
- selected_operator = nil
199
- # get the operators
200
- @sas_plan.each do |name|
201
- if Task::is_global_operator?(name)
202
- if selected_operator != nil
203
- selected_operator.global_op = find_global_operator(name[1, name.length-2])
204
- raise Exception, 'not found: ' + name if selected_operator.global_op.nil?
205
- end
206
- selected_operator = nil
207
- elsif Task::is_goal_operator?(name)
208
- # TODO -- handle "goal-verifier" action for handling "sometime" constraint
209
- else
210
- selected_operator = find_operator(name[1, name.length-2])
211
- raise Exception, 'not found: ' + name if selected_operator.nil?
212
- plan << selected_operator
213
- end
214
- end
215
- # remove global variable from plan's operators
216
- plan.each { |op| op.remove_global_variable(@global_variable) }
217
-
218
- return plan
219
- end
220
-
221
- def set_predecessors(plan)
222
- (plan.length-1).downto(0) do |i|
223
- op1 = plan[i]
224
- op1.prevails.each do |p|
225
- (i-1).downto(0) do |j|
226
- op2 = plan[j]
227
- if op2.support_assignment?(p[:var], p[:prevail])
228
- op1.predecessors << op2
229
- elsif op2.threat_assignment?(p[:var], p[:prevail])
230
- break
231
- end
232
- end
233
- end
234
- op1.pre_posts.each do |p|
235
- (i-1).downto(0) do |j|
236
- op2 = plan[j]
237
- if op2.threatened_by_assignment?(p[:var], p[:post])
238
- op1.predecessors << op2
239
- end
240
- end
241
- end
242
- end
243
- end
244
-
245
- def remove_transitive_predecessors(plan)
246
- # foreach operator, remove duplicates in predecessors list
247
- @operators.each { |op| op.predecessors.uniq! }
248
-
249
- # remove "transitive" predecessor link
250
- plan.each do |op|
251
- valid_predecessors = []
252
- op.predecessors.each do |pred_op|
253
- valid_predecessors << pred_op if not op.has_transitive_predecessors?(pred_op)
254
- end
255
- op.predecessors = valid_predecessors
256
- end
257
- end
258
-
259
- def add_adversary_predecessors(plan)
260
- # search primary operators (the last operators that provide the goal)
261
- primary_ops = []
262
- @goal.each do |var|
263
- (plan.length-1).downto(0) do |i|
264
- if plan[i].support_assignment?(var, var.goal)
265
- primary_ops << [var, var.goal, i, plan[i]]
266
- break;
267
- end
268
- end
269
- end
270
- # search adversary operators (all operators that threat the goal)
271
- primary_ops.each do |variable, value, last_index, operator|
272
- (last_index-1).downto(0) do |j|
273
- next if operator == plan[j]
274
- operator.predecessors << plan[j] if plan[j].threat_assignment?(variable, value)
275
- end
276
- end
277
- #self.dump_predecessors(plan)
278
- end
279
-
280
- # given a state, find the first applicable "global" operator
281
- def is_state_satisfy_global?(state)
282
- @global_operators.each { |op| return op if state.applicable?(op) }
283
- return nil
284
- end
285
-
286
- def plan_with_trajectory_to_partial_order
287
- begin
288
- # get the operators
289
- plan = collect_operators
290
-
291
- # supporting operators
292
- set_predecessors(plan)
293
-
294
- # adversary predecessors
295
- add_adversary_predecessors(plan)
296
-
297
- # fill-in blank-precondition (pre := -1) by simulating the plan
298
- states = simulate_plan(plan)
299
-
300
- # generate the stage-workflow
301
- stages, states = to_stage_workflow(plan, states)
302
- #puts '==> failed' if stages.nil?
303
- return nil if stages.nil?
304
-
305
- # generate the partial-order workflow
306
- if not stages.nil?
307
- to_partial_order_workflow(stages, states)
308
- end
309
-
310
- #stages.each { |stage|
311
- # puts "-- stage -- "
312
- # stage.each { |op| puts op.name }
313
- #}
314
-
315
- remove_transitive_predecessors(plan)
316
-
317
- plan = remove_sometime_operators(plan)
318
-
319
- return plan
320
-
321
- rescue Exception => e
322
- $stderr.puts e.to_s
323
- $stderr.puts e.backtrace
324
- end
325
- return nil
326
- end
327
-
328
- def remove_sometime_operators(plan, parallel=true)
329
- sometimes = []
330
- plan.each { |op| sometimes << op if Task.is_sometime_operator?(op.name) }
331
- plan.delete_if { |op| sometimes.include?(op) }
332
- plan.each do |op|
333
- sometimes.each do |op1|
334
- index = op.predecessors.index(op1)
335
- if not index.nil?
336
- op1.predecessors.each { |op2|
337
- op.predecessors << op2 if plan.include?(op2)
338
- }
339
- op1.predecessors.delete_at(index)
340
- op1.predecessors.uniq!
341
- end
342
- end
343
- end
344
- plan
345
- end
346
-
347
- def set_global(state, valid=true)
348
- if valid
349
- state[@global_variable.index] = (@init[@global_variable.index] == 0 ? 1 : 0)
350
- else
351
- state[@global_variable.index] = @init[@global_variable.index]
352
- end
353
- end
354
-
355
- def to_partial_order_workflow(stages, states)
356
- # splitting -- add required predecessor-links between operators of two stages
357
- (stages.length-1).downto(1) do |i|
358
- current_stage = stages[i]
359
- prev_stage = stages[i-1]
360
- prev_prev_state = states[i-1]
361
- current_stage.each do |op1|
362
- prev_stage.each do |op2|
363
- if op1.predecessors.index(op2).nil?
364
- new_state = prev_prev_state.clone
365
- # generate the next state if "op2" is not included
366
- prev_stage.each { |op3| new_state.apply!(op3) if op3 != op2 }
367
- self.set_global(new_state, false)
368
- if self.is_state_satisfy_global?(new_state).nil?
369
- op1.predecessors << op2
370
- else
371
- new_state.apply!(op1)
372
- self.set_global(new_state, false)
373
- if self.is_state_satisfy_global?(new_state).nil?
374
- op1.predecessors << op2
375
- else
376
- #puts op2.name + " || " + op1.name
377
- end
378
- end
379
- end
380
- end
381
-
382
- has_predecessor = false
383
- prev_stage.each { |op2|
384
- has_predecessor = (not op1.predecessors.index(op2).nil?)
385
- break if has_predecessor
386
- }
387
- if not has_predecessor
388
- promotion = false
389
- if prev_prev_state.applicable?(op1) and not prev_stage.threaten?(op1)
390
- new_state = prev_prev_state.apply(op1)
391
- self.set_global(new_state, false)
392
- promotion = (not self.is_state_satisfy_global?(new_state).nil?)
393
- end
394
-
395
- if promotion
396
- prev_stage << op1
397
- else
398
- prev_stage.each { |op2| op1.predecessors << op2 }
399
- end
400
- end
401
- end
402
- end
403
-
404
- # promotion
405
- end
406
-
407
- # TODO -- Fix bugs
408
- def to_stage_workflow(plan, states)
409
- begin
410
- current_state = states[states.length-1].clone
411
- operators = plan.reverse
412
- counter = 0
413
- stages = []
414
- states = []
415
-
416
- while not current_state.equals?(@init) and operators.length > 0 and counter < plan.length
417
- selected_ops, current_state = search_supporting_operators(current_state, operators)
418
- if selected_ops.length > 0
419
- stages << selected_ops
420
- selected_ops.each { |op| operators.delete(op) }
421
- states << current_state
422
- end
423
- counter += 1
424
- end
425
-
426
- begin
427
- #current_state.each_index { |i|
428
- # next if current_state[i] == @init[i]
429
- # puts i.to_s + ' ' + current_state[i].to_s + ' != ' + @init[i].to_s
430
- #}
431
- #puts current_state.inspect
432
- #puts @init.inspect
433
- return nil, nil
434
- end if not current_state.equals?(@init)
435
- return stages.reverse, states.reverse
436
- rescue Exception => e
437
- $stderr.puts e.to_s
438
- $stderr.puts e.backtrace
439
- end
440
- end
441
-
442
- def search_supporting_operators(state, operators) #, selected_operators)
443
- selected_operators = OperatorLayer.new
444
- not_candidates = OperatorLayer.new
445
- candidates = OperatorLayer.new
446
- operators.each do |op|
447
- if state.applicable_reverse?(op) and
448
- not candidates.threat?(op) and
449
- not candidates.threat_reverse?(op) and
450
- not candidates.require?(op)
451
- candidates << op
452
- else
453
- not_candidates << op
454
- end
455
- end
456
-
457
- result_state = state.clone
458
- candidates.each do |op|
459
- if not_candidates.require?(op)
460
- not_candidates << op
461
- else
462
- new_state = state.apply_reverse(op)
463
- self.set_global(new_state, false)
464
- if not self.is_state_satisfy_global?(new_state).nil?
465
- new_result_state = result_state.apply_reverse(op)
466
- self.set_global(new_result_state, false)
467
- if not self.is_state_satisfy_global?(new_result_state).nil?
468
- result_state = new_result_state
469
- selected_operators << op
470
- end
471
- end
472
- end
473
- end
474
- return selected_operators, result_state
475
- end
476
-
477
-
478
- def simulate_plan(plan)
479
- state = @init.clone
480
- self.set_global(state, true)
481
- states = [state.clone]
482
- plan.each do |op|
483
- op.pre_posts.each { |p| p[:pre] = state[p[:var].index] if p[:pre] < 0 }
484
- state.apply!(op)
485
- state.apply!(op.global_op)
486
- states.push(state.clone)
487
- end
488
- return states
489
- end
490
-
491
- # Return the partial-order plan from a total-order plan kept in @sas_plan
492
- # with an assumption that the planning task does not have trajectory constraints
493
- #
494
- # @return - a partial-order plan
495
- def plan_without_trajectory_to_partial_order
496
- # get the operators
497
- plan = collect_operators
498
-
499
- # supporting operators
500
- set_predecessors(plan)
501
- #self.dump_predecessors(plan)
502
-
503
- # add adversary predecessors
504
- add_adversary_predecessors(plan)
505
-
506
- # remove transitive predecessors
507
- remove_transitive_predecessors(plan)
508
-
509
- plan = remove_sometime_operators(plan)
510
-
511
- return plan
512
- end
513
-
514
- def dump_predecessors(plan)
515
- puts "=== predecessors ==="
516
- plan.each { |op|
517
- puts op.name
518
- op.predecessors.each { |op2| puts "\t" + op2.name }
519
- }
520
- end
521
-
522
- # Test if the given state contains goal or not
523
- # @param - state: the state to be tested
524
- # @return True if the given state contains goal, otherwise False
525
- def at_goal?(state, details=false)
526
- # TODO -- test it
527
- if not details
528
- @goal.each { |var| return false if state[var.index] != var.goal }
529
- return true
530
- else
531
- valid = true
532
- @goal.each do |var|
533
- if state[var.index] != var.goal
534
- valid = false
535
- end
536
- end
537
- return valid
538
- end
539
- end
540
- end
541
-
542
- class OperatorLayer < Array
543
- def applied_to(state)
544
- self.each { |op| return false if not state.apply!(op) }
545
- return true
546
- end
547
-
548
- def applied_reverse_to(state)
549
- self.each { |op| state.apply_reverse!(op) }
550
- end
551
-
552
- def threaten?(operator)
553
- self.each { |op| return true if operator.threat?(op) }
554
- return false
555
- end
556
-
557
- def threat?(operator)
558
- self.each { |op| return true if op.threat?(operator) }
559
- return false
560
- end
561
-
562
- def threat_reverse?(operator)
563
- self.each { |op| return true if op.threat_reverse?(operator) }
564
- return false
565
- end
566
-
567
- def support?(operator)
568
- self.each { |op| return true if op.support?(operator) }
569
- return false
570
- end
571
-
572
- # an operator is required iff there's a casual to any operator in this layer
573
- def require?(operator)
574
- self.each { |op| return true if not op.predecessors.index(operator).nil? }
575
- return false
576
- end
577
- end
578
-
579
- class Operator
580
- attr_reader :name, :prevails, :pre_posts, :cost, :is_global
581
- attr_accessor :predecessors # all-operators that preceed 'self'
582
- attr_accessor :global_op
583
- attr_accessor :sfw_operator
584
-
585
- def initialize(params={})
586
- if params.has_key?(:io)
587
- self.read_io(params[:io], params[:variables])
588
- else
589
- self.read_parameters(params)
590
- end
591
- @predecessors = []
592
- @is_global = Task.is_global_operator?(@name)
593
- end
594
-
595
- def has_transitive_predecessors?(operator)
596
- @predecessors.each do |pre_op|
597
- return true if pre_op != operator and
598
- pre_op.has_predecessor?(operator)
599
- end
600
- return false
601
- end
602
-
603
- def has_predecessor?(operator)
604
- if not @predecessors.index(operator).nil?
605
- return true
606
- else
607
- @predecessors.each { |op| return true if op.has_predecessor?(operator) }
608
- end
609
- return false
610
- end
611
-
612
- def support?(operator)
613
- # TODO -- test it
614
- @pre_posts.each do |p1|
615
- operator.prevails.each { |p2|
616
- return true if p1[:var] == p2[:var] and
617
- p1[:post] == p2[:prevail]
618
- }
619
- operator.pre_posts.each { |p2|
620
- return true if p2[:pre] >= 0 and
621
- p1[:var] == p2[:var] and
622
- p1[:post] == p2[:pre]
623
- }
624
- end
625
- return false
626
- end
627
-
628
- def threatened_by_assignment?(variable, value)
629
- return false if value < 0
630
- @prevails.each { |p| return true if p[:var] == variable and p[:prevail] != value }
631
- @pre_posts.each { |p| return true if p[:var] == variable and p[:pre] != value }
632
- return false
633
- end
634
-
635
- def support_assignment?(variable, value)
636
- return true if value < 0
637
- @pre_posts.each do |p|
638
- return true if p[:var] == variable and p[:post] == value
639
- end
640
- return false
641
- end
642
-
643
- def threat_assignment?(variable, value)
644
- return false if value < 0
645
- @pre_posts.each do |p|
646
- return true if p[:var] == variable and p[:post] != value
647
- end
648
- return false
649
- end
650
-
651
- def threat_prevails?(prevails)
652
- return false if prevails.length <= 0 or pre_posts.length <= 0
653
- prevails.each do |p1|
654
- @pre_posts.each do |p2|
655
- if p1[:var] == p2[:var]
656
- #begin puts p1[:var].name + '>>' + p1[:prevail].to_s + ' ' + p2[:post].to_s; return true; end if p1[:prevail] != p2[:post]
657
- return true if p1[:prevail] != p2[:post]
658
- end
659
- end
660
- end
661
- return false
662
- end
663
-
664
- def mutex?(operator)
665
- # TODO -- test it
666
- @prevails.each do |p1|
667
- operator.pre_posts.each { |p2| return false if p1[:var] == p2[:var] }
668
- end
669
- @pre_posts.each do |p1|
670
- operator.prevails.each { |p2| return false if p1[:var] == p2[:var] }
671
- operator.pre_posts.each { |p2| return false if p1[:var] == p2[:var] and p1[:post] != p2[:post] }
672
- end
673
- return true
674
- end
675
-
676
- def threat?(operator)
677
- @pre_posts.each do |p1|
678
- operator.prevails.each { |p2|
679
- if p1[:var] == p2[:var] and p1[:post] != p2[:prevail]
680
- #puts "\t" + self.name + " -- " + operator.name
681
- #puts "\t" + p1[:var].name + " p1:" + p1[:post].to_s + " p2:" + p2[:prevail].to_s
682
- return true
683
- end
684
- }
685
- operator.pre_posts.each { |p2| return true if p1[:var] == p2[:var] and p1[:post] != p2[:post] }
686
- end
687
- return false
688
- end
689
-
690
- def threat_reverse?(operator)
691
- @pre_posts.each do |p1|
692
- operator.prevails.each { |p2|
693
- return true if p1[:var] == p2[:var] and p1[:pre] != p2[:prevail]
694
- }
695
- operator.pre_posts.each { |p2|
696
- return true if p1[:var] == p2[:var] and p1[:pre] != p2[:pre]
697
- }
698
- end
699
- return false
700
- end
701
-
702
- def remove_global_variable(var)
703
- @pre_posts.delete_if { |p| p[:var] == var }
704
- end
705
-
706
- def write_io(io)
707
- io.puts('begin_operator', @name, @prevails.length.to_s)
708
- @prevails.each { |p| io.puts(p[:var].index.to_s + ' ' + p[:prevail].to_s) }
709
- io.puts(@pre_posts.length.to_s)
710
- @pre_posts.each { |p| io.puts('0 ' + p[:var].index.to_s + ' ' + p[:pre].to_s + ' ' + p[:post].to_s) }
711
- io.puts(@cost, 'end_operator')
712
- end
713
-
714
- def dump
715
- puts @name
716
- @prevails.each { |p| print p[:var].index.to_s + '=' + p[:prevail].to_s + ";" }
717
- puts '' if @prevails.length > 0
718
- @pre_posts.each { |p| print p[:var].index.to_s + '=' + p[:pre].to_s + ',' + p[:post].to_s + ';' }
719
- puts ''
720
- #puts @cost
721
- end
722
-
723
- protected
724
- def read_io(io, variables)
725
- io.gets # begin_operator
726
- @name = io.gets.chop
727
- @prevails = []
728
- for j in 1..io.gets.to_i
729
- tuple = io.gets.split(' ')
730
- @prevails << { :var => variables[tuple[0].to_i],
731
- :prevail => tuple[1].to_i }
732
- end
733
- @pre_posts = []
734
- for j in 1..io.gets.to_i
735
- tuple = io.gets.split(' ')
736
- @pre_posts << { :var => variables[tuple[1].to_i],
737
- :pre => tuple[2].to_i,
738
- :post => tuple[3].to_i }
739
- end
740
- @cost = io.gets.to_i
741
- io.gets # end_operator
742
- end
743
-
744
- def read_parameters(params) #name, prevails, pre_posts, cost, is_global=false)
745
- @name = params[:name]
746
- @prevails = params[:prevails]
747
- @pre_posts = params[:pre_posts]
748
- @cost = params[:cost]
749
- end
750
- end
751
-
752
- class Axiom < Operator
753
- def initialize(params={})
754
- if params.has_key?(:io)
755
- self.read_io(params[:io], params[:variables])
756
- else
757
- self.read_parameters(params)
758
- end
759
- @predecessors = []
760
- @is_global = false
761
- end
762
-
763
- def write_io(io)
764
- io.puts('begin_rule', @prevails.length)
765
- @prevails.each { |p| io.puts(p[:var].index.to_s + ' ' + p[:prevail].to_s) }
766
- @pre_posts.each { |p| io.puts(p[:var].index.to_s + ' ' + p[:pre].to_s + ' ' + p[:post].to_s) }
767
- io.puts('end_rule')
768
- end
769
-
770
- protected
771
- def read_io(io, variables)
772
- io.gets # begin_rule
773
- @name = :axiom
774
- prevails = []
775
- for j in 1..io.gets.to_i
776
- tuple = io.gets.split(' ')
777
- prevails << { :var => variables[tuple[0].to_i],
778
- :prevail => tuple[1].to_i }
779
- end
780
- pre_posts = []
781
- tuple = io.gets.split(' ')
782
- pre_posts << { :var => variables[tuple[0].to_i],
783
- :pre => tuple[1].to_i,
784
- :post => tuple[2].to_i }
785
- io.gets # end_rule
786
- end
787
-
788
- def read_parameters(params) #name, prevails, pre_posts)
789
- @name = params[:name]
790
- @prevails = params[:prevails]
791
- @pre_posts = params[:pre_posts]
792
- @cost = 0
793
- end
794
- end
795
-
796
- class State < Array
797
- def write_io(io)
798
- io.puts('begin_state')
799
- for i in 0..(self.length-1)
800
- io.puts(self[i])
801
- end
802
- io.puts('end_state')
803
- end
804
-
805
- def apply_reverse!(operator, do_check=false)
806
- return false if do_check and not self.applicable_reverse?(operator)
807
- operator.pre_posts.each { |p| return false if p[:pre] < 0 }
808
- operator.pre_posts.each { |p| self[p[:var].index] = p[:pre] }
809
- return true
810
- end
811
-
812
- def apply_reverse(operator, do_check=false)
813
- return nil if do_check and not self.applicable_reverse?(operator)
814
- state = self.clone
815
- state.apply_reverse!(operator)
816
- return state
817
- end
818
-
819
- def apply!(operator, do_check=false)
820
- return false if do_check and not self.applicable?(operator)
821
- operator.pre_posts.each { |p| self[p[:var].index] = p[:post] }
822
- return true
823
- end
824
-
825
- def apply(operator, do_check=false)
826
- return nil if do_check and not self.applicable?(operator)
827
- state = self.clone
828
- state.apply!(operator)
829
- return state
830
- end
831
-
832
- def applicable_reverse?(operator)
833
- operator.prevails.each { |p| return false if self[p[:var].index] != p[:prevail] }
834
- operator.pre_posts.each { |p| return false if self[p[:var].index] != p[:post] }
835
- return true
836
- end
837
-
838
- def applicable?(operator)
839
- operator.prevails.each { |p| return false if self[p[:var].index] != p[:prevail] }
840
- operator.pre_posts.each { |p| return false if p[:pre] >= 0 and self[p[:var].index] != p[:pre] }
841
- return true
842
- end
843
-
844
- def clone
845
- state = State.new
846
- for i in 0..(self.length-1)
847
- state[i] = self[i]
848
- end
849
- return state
850
- end
851
-
852
- def satisfy?(goal)
853
- goal.each { |var| return false if self[var.index].to_i != var.goal.to_i }
854
- return true
855
- end
856
-
857
- def equals?(state)
858
- return false if state.length != self.length
859
- for i in 0..(self.length-1)
860
- return false if self[i] != state[i]
861
- end
862
- return true
863
- end
864
-
865
- def dump
866
- for i in 0..(self.length-1)
867
- print i.to_s + '=' + self[i].to_s + ','
868
- end
869
- puts ''
870
- end
871
- end
872
-
873
- class Goal < Array
874
- def initialize(params={})
875
- if params.has_key?(:io)
876
- self.read_io(params[:io], params[:variables])
877
- end
878
- end
879
-
880
- def write_io(io)
881
- io.puts('begin_goal')
882
- io.puts(self.length)
883
- self.each { |var| io.puts(var.index.to_s + ' ' + var.goal.to_s) }
884
- io.puts('end_goal')
885
- end
886
-
887
- def dump
888
- self.each { |var| print var.index.to_s + '=' + var.goal.to_s + ',' }
889
- puts ''
890
- end
891
-
892
- protected
893
- def read_io(io, variables)
894
- io.gets # begin_goal
895
- total = io.gets.to_i
896
- for i in 1..total
897
- tuple = io.gets.split(' ')
898
- variable = variables[tuple[0].to_i]
899
- variable.goal = tuple[1].to_i
900
- self << variable
901
- end
902
- io.gets # end_goal
903
- end
904
- end
905
-
906
- class Prevail
907
- attr_accessor :var, :prevail
908
- end
909
-
910
- class PrePost
911
- attr_accessor :var, :pre, :post
912
- end
913
-
914
- class Variable
915
- attr_accessor :init, :goal
916
- attr_reader :name, :axiom_layer, :length, :index, :is_global, :is_goal
917
-
918
- def initialize(params={})
919
- if params.has_key?(:io)
920
- self.read_io(params[:io])
921
- else
922
- self.read_parameters(params)
923
- end
924
- @is_global = Task::is_global_variable?(@name)
925
- @is_goal = Task::is_goal_variable?(@name)
926
- @index = params[:index]
927
- end
928
-
929
- def write_io(io)
930
- io.puts('begin_variable')
931
- io.puts(@name)
932
- io.puts(@axiom_layer.to_s)
933
- io.puts(@length.to_s)
934
- for i in 0..(@length-1)
935
- io.puts(i)
936
- end
937
- io.puts('end_variable')
938
- end
939
-
940
- protected
941
- def read_io(io)
942
- io.gets # begin_variable
943
- @name = io.gets.chop
944
- @axiom_layer = io.gets.to_i
945
- @length = io.gets.to_i
946
- for j in 1..@length
947
- io.gets
948
- end
949
- io.gets # end_variable
950
- end
951
-
952
- def read_parameters(params)
953
- @name = params[:name]
954
- @axiom_layer = params[:axiom_layer]
955
- @length = params[:length]
956
- @init = @goal = -1
957
- @is_global = false
958
- end
959
- end
960
-
961
- end
962
- end
963
-
964
- if __FILE__ == $0
965
- sas = Nuri::Sas::Task.new($1)
966
- end