Object subclass: #FactorioBillOfMaterials instanceVariableNames: 'finishedItem finishedQuantity intermediateItemsAndQuantities rawItemsAndQuantities subsetBills plannedDuration' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 19:46'! finishedItem ^ finishedItem! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 19:46'! finishedItem: anObject finishedItem := anObject.! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 19:46'! finishedQuantity ^ finishedQuantity! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 19:46'! finishedQuantity: anObject finishedQuantity := anObject.! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 19:46'! intermediateItemsAndQuantities ^ intermediateItemsAndQuantities! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 19:46'! intermediateItemsAndQuantities: anObject intermediateItemsAndQuantities := anObject.! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/29/2021 10:15'! plannedDuration ^ plannedDuration! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/29/2021 10:15'! plannedDuration: anObject plannedDuration := anObject.! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 19:46'! rawItemsAndQuantities ^ rawItemsAndQuantities! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 19:46'! rawItemsAndQuantities: anObject rawItemsAndQuantities := anObject.! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 20:09'! subsetBills ^ subsetBills! ! !FactorioBillOfMaterials methodsFor: 'accessing' stamp: 'raa 5/13/2021 20:09'! subsetBills: anObject subsetBills := anObject.! ! !FactorioBillOfMaterials methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 10:24'! computeRawPerSecond | rawPerSecond | rawPerSecond _ Dictionary new. {self},subsetBills do: [ :bom | bom rawItemsAndQuantities keysAndValuesDo: [ :k :qty | (FactorioLUA LuaData isFluid: k) ifFalse: [ rawPerSecond at: k add: qty / plannedDuration asFloat ]. ]. ]. ^rawPerSecond! ! !FactorioBillOfMaterials methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 10:32'! computeSubsetBillsFrom: others | mine rawPerSecond | subsetBills _ OrderedCollection new. mine _ self requiredItems. others do: [ :e | (e finishedItem = finishedItem or: [subsetBills anySatisfy: [ :sb | e finishedItem = sb finishedItem]]) ifFalse: [ (e requiredItems allSatisfy: [ :oi | mine includes: oi]) ifTrue: [ rawPerSecond _ self computeRawPerSecond. e computeRawPerSecond keysAndValuesDo: [ :k :v | rawPerSecond at: k add: v ]. (rawPerSecond anySatisfy: [ : rps | rps > 30]) ifTrue: [ "self halt" ] ifFalse: [ e computeTimeEstimate first second > 5 ifTrue: [ "self halt" ] ifFalse: [ e ? 'assembling-machine-1' ifTrue: ["self halt"]. subsetBills add: e ]. ]. ] ] ]! ! !FactorioBillOfMaterials methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 10:27'! computeTimeEstimate | rdata recipes time timeList secs | timeList _ OrderedCollection new. recipes _ FactorioLUA LuaData recipesByResult. {self},subsetBills do: [ :bom | time _ 0. bom intermediateItemsAndQuantities keysAndValuesDo: [ :k :qty | rdata _ (recipes at: k) first. secs _ rdata craftingSecondsFor: k qty: qty at: #modded. time _ time + secs. ]. timeList add: {bom finishedItem. time / plannedDuration}. ]. ^timeList! ! !FactorioBillOfMaterials methodsFor: 'as yet unclassified' stamp: 'raa 5/13/2021 20:09'! initialize super initialize. intermediateItemsAndQuantities _ Dictionary new. rawItemsAndQuantities _ Dictionary new. subsetBills _ OrderedCollection new.! ! !FactorioBillOfMaterials methodsFor: 'as yet unclassified' stamp: 'raa 5/13/2021 19:51'! numberOfComponents ^intermediateItemsAndQuantities size + rawItemsAndQuantities size! ! !FactorioBillOfMaterials methodsFor: 'as yet unclassified' stamp: 'raa 5/13/2021 20:07'! printOn: s super printOn: s. s space; print: finishedItem; space; print: finishedQuantity.! ! !FactorioBillOfMaterials methodsFor: 'as yet unclassified' stamp: 'raa 5/13/2021 20:12'! requiredItems ^intermediateItemsAndQuantities keys, rawItemsAndQuantities keys! ! !FactorioBillOfMaterials methodsFor: 'as yet unclassified' stamp: 'raa 5/13/2021 19:53'! totalRawInput ^rawItemsAndQuantities detectSum: [ :e | e]! ! Notification subclass: #FactorioBuildInterrupted instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! Object subclass: #FactorioBuildRequirement instanceVariableNames: 'obj1 obj2 action depth visits successes' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! action ^ action! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! action: anObject action := anObject.! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! depth ^ depth! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! depth: anObject depth := anObject.! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! obj1 ^ obj1! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! obj1: anObject obj1 := anObject.! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! obj2 ^ obj2! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! obj2: anObject obj2 := anObject.! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/28/2021 09:03'! successes ^successes! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! successes: anObject successes := anObject.! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! visits ^ visits! ! !FactorioBuildRequirement methodsFor: 'accessing' stamp: 'raa 4/22/2021 11:24'! visits: anObject visits := anObject.! ! !FactorioBuildRequirement methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 11:32'! initialize super initialize. depth _ visits _ successes _ 0.! ! !FactorioBuildRequirement methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 08:55'! newItemCount ^action = #buildSupplierInserterConsumer: ifTrue: [2] ifFalse: [1]! ! !FactorioBuildRequirement methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 11:30'! successPlusOne successes _ successes + 1! ! !FactorioBuildRequirement methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 20:27'! visitsPlusOne visits _ visits + 1. "visits \\ 1000 = 555 ifTrue: [ (obj1 ? 'plastic-bar' and: [obj2 ? 'low-density-structure']) ifTrue: [self halt] ]" ! ! Object subclass: #FactorioBuilder instanceVariableNames: 'map builds counter ct2 start variations luaData maxDepthCount blueprintString runQuiet indexTimes levelRectangles' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioBuilder methodsFor: 'belts and pipes' stamp: 'raa 4/25/2021 20:17'! allPointsFrom: a to: b do: aBlock | incr pt loop | aBlock value: a. a = b ifTrue: [^self]. incr _ (b - a) sign. pt _ a. loop _ 0. [ pt _ pt + incr. aBlock value: pt. pt = b ifTrue: [^self]. (loop _ loop + 1) > 100 ifTrue: [self halt]. ] repeat. ! ! !FactorioBuilder methodsFor: 'belts and pipes' stamp: 'raa 4/27/2021 20:08'! beltFrom: inputEnd to: outputEnd for: itemName | dir incr pt ct okList | incr _ (inputEnd - outputEnd) sign. pt _ outputEnd. dir _ incr x = 0 ifTrue: [incr y = 1 ifTrue: [4] ifFalse: [0]] ifFalse: [incr x = 1 ifTrue: [2] ifFalse: [6]]. ct _ 0. pt _ outputEnd. okList _ OrderedCollection new. [ (map isLocationFree: pt) ifTrue: [okList add: pt] ifFalse: [okList add: #x]. pt = inputEnd ] whileFalse: [ pt _ pt + incr. (ct _ ct + 1) > 100 ifTrue: [self halt]. ]. (okList includes: #x) ifTrue: ["self halt"]. 2 to: okList size - 1 do: [ :i | ((okList at: i-1) = #x and: [(okList at: i+1) = #x]) ifTrue: [okList at: i put: #x] ]. self buildMultipleUndergroundBelts: okList for:itemName. ^self constantCombinatorAt: inputEnd with: incr! ! !FactorioBuilder methodsFor: 'belts and pipes' stamp: 'raa 4/27/2021 07:34'! beltUndergroundFrom: inputEnd to: outputEnd for: itemName | belt dir incr pt ct | ct _ 0. incr _ (inputEnd - outputEnd) sign. pt _ outputEnd. dir _ incr x = 0 ifTrue: [incr y = 1 ifTrue: [4] ifFalse: [0]] ifFalse: [incr x = 1 ifTrue: [2] ifFalse: [6]]. (map isLocationFree: pt) ifFalse: [self halt]. belt _ FactorioUndergroundBelt new map: map. belt addMaterial: itemName; direction: (dir \\ 8); inputOrOutput: 'output'; location: pt. map addEntity: belt. belt _ FactorioUndergroundBelt new map: map. belt addMaterial: itemName; direction: (dir + 4 \\ 8); inputOrOutput: 'input'; location: inputEnd. map addEntity: belt. ! ! !FactorioBuilder methodsFor: 'belts and pipes' stamp: 'raa 4/27/2021 20:09'! buildFinalConnections | bounds itemName machine pt incr dir inserter combinator fluids items options bCount sorted debug | bounds _ map totalBounds. fluids _ map finalConnections select: [ :e | luaData isFluid: e second]. items _ map finalConnections reject: [ :e | luaData isFluid: e second]. fluids,items do: [ :tuple | machine _ tuple first. itemName _ tuple second. options _ OrderedCollection new. debug _ OrderedCollection new. self shortestRoutesFrom: machine to: bounds do: [ :pt0 :pt1 | incr _ (pt0 - pt1) sign. dir _ incr x = 0 ifTrue: [incr y = 1 ifTrue: [4] ifFalse: [0]] ifFalse: [incr x = 1 ifTrue: [2] ifFalse: [6]]. debug add: {pt0. pt1. incr. dir}. (luaData isFluid: itemName) ifTrue: [ machine direction: dir. ((map isLocationFree: pt1) and: [machine isFluidInput: pt1]) ifTrue: [ options add: {pt0. pt1}. ] ] ifFalse: [ ((map isLocationFree: pt1) and: [map isLocationFree: pt1+incr]) ifTrue: [ options add: {pt0. pt1}. ]. ]. #continue ]. sorted _ OrderedCollection new. options withIndexDo: [ :e :index | bCount _ 0. self allPointsFrom: e first to: e second do: [ :z | map entities do: [ :en | (en isTransport and: [en location = z]) ifTrue: [bCount _ bCount + 1]] . ]. sorted add: {e. bCount. index}. ]. sorted _ sorted sortBy: [ :a :b | a second < b second or: [a second = b second and: [a third < b third]] ]. options _ OrderedCollection new. sorted do: [ :e | options add: e first first; add: e first second]. (machine isKindOf: FactorioAssembler) ifTrue: ["self halt"]. combinator _ nil. options pairsDo: [ :pt0 :pt1 | combinator ifNil: [ incr _ (pt0 - pt1) sign. pt _ pt1. dir _ incr x = 0 ifTrue: [incr y = 1 ifTrue: [4] ifFalse: [0]] ifFalse: [incr x = 1 ifTrue: [2] ifFalse: [6]]. (luaData isFluid: itemName) ifTrue: [ machine direction: dir. (machine isKindOf: FactorioAssembler) ifTrue: ["self halt"]. ((map isLocationFree: pt) and: [machine isFluidInput: pt]) ifTrue: [ combinator _ self pipeFrom: pt0 to: pt for: itemName. ] ] ifFalse: [ ((map isLocationFree: pt) and: [map isLocationFree: pt+incr]) ifTrue: [ inserter _ FactorioInserter new map: map. inserter direction: dir; gap: 1; location: pt. map addEntity: inserter. pt _ pt + incr. (pt0 dist: pt) > 2 ifTrue: [ combinator _ self beltFrom: pt0 to: pt for: itemName. ] ifFalse: [ combinator _ self constantCombinatorAt: pt with: incr ] ]. ]. combinator ifNotNil: [ combinator addFilter: itemName qty: 1. ]. ]. ]. ].! ! !FactorioBuilder methodsFor: 'belts and pipes' stamp: 'raa 4/27/2021 07:32'! buildMultipleUndergroundBelts: arg for: itemName | before end okList max | okList _ arg. max _ 10. [okList size > 1] whileTrue: [ before _ okList size. (okList size <= max and: [okList first ~= #x] and: [okList last ~= #x]) ifTrue: [ ^self beltUndergroundFrom: okList last to: okList first for: itemName. ] ifFalse: [ end _ nil. 3 to: (max+1 min: okList size) do: [ :i | ((okList at: i) ~= #x and: [(okList at: i-1) ~= #x]) ifTrue: [ end _ i-1. ]. ]. end ifNotNil: [ self beltUndergroundFrom: (okList at: end) to: okList first for: itemName. okList _ okList allButFirst: end. ]. ]. before = okList size ifTrue: [^self]. ]. ! ! !FactorioBuilder methodsFor: 'belts and pipes' stamp: 'raa 4/27/2021 07:43'! buildMultipleUndergroundPipes: arg for: itemName | before end okList | okList _ arg. [okList size > 1] whileTrue: [ before _ okList size. (okList size <= 11 and: [okList first ~= #x] and: [okList last ~= #x]) ifTrue: [ ^self pipeUndergroundFrom: okList last to: okList first for: itemName. ] ifFalse: [ end _ nil. 3 to: (12 min: okList size) do: [ :i | ((okList at: i) ~= #x and: [(okList at: i-1) ~= #x]) ifTrue: [ end _ i-1. ]. ]. end ifNotNil: [ self pipeUndergroundFrom: (okList at: end) to: okList first for: itemName. okList _ okList allButFirst: end. ]. ]. before = okList size ifTrue: [^self]. ]. ! ! !FactorioBuilder methodsFor: 'belts and pipes' stamp: 'raa 4/27/2021 20:14'! constantCombinatorAt: inputEnd with: incr | combinator pt | pt _ inputEnd. [ (map isLocationFree: pt) ifTrue: [ combinator _ FactorioConstantCombinator new map: map. combinator location: pt. map addEntity: combinator. ^combinator ] ifFalse: [ pt _ pt + incr ]. ] repeat. ! ! !FactorioBuilder methodsFor: 'belts and pipes' stamp: 'raa 4/27/2021 07:44'! pipeFrom: inputEnd to: outputEnd for: itemName | dir incr pt ct okList combinator | incr _ (inputEnd - outputEnd) sign. pt _ outputEnd. dir _ incr x = 0 ifTrue: [incr y = 1 ifTrue: [4] ifFalse: [0]] ifFalse: [incr x = 1 ifTrue: [2] ifFalse: [6]]. ct _ 0. pt _ outputEnd. okList _ OrderedCollection new. [ (map isLocationFree: pt) ifTrue: [okList add: pt] ifFalse: [okList add: #x]. pt = inputEnd ] whileFalse: [ pt _ pt + incr. (ct _ ct + 1) > 100 ifTrue: [self halt]. ]. 2 to: okList size - 1 do: [ :i | ((okList at: i -1) = #x and: [(okList at: i+1) = #x]) ifTrue: [okList at: i put: #x] ]. self buildMultipleUndergroundPipes: okList for: itemName. pt _ inputEnd. combinator _ nil. [ (map isLocationFree: pt) ifTrue: [ combinator _ FactorioConstantCombinator new map: map. combinator location: pt. map addEntity: combinator. ] ifFalse: [ pt _ pt + incr ]. combinator isNil ] whileTrue. ^combinator ! ! !FactorioBuilder methodsFor: 'belts and pipes' stamp: 'raa 4/27/2021 07:46'! pipeUndergroundFrom: inputEnd to: outputEnd for: itemName | belt dir incr pt ct | ct _ 0. incr _ (inputEnd - outputEnd) sign. pt _ outputEnd. dir _ incr x = 0 ifTrue: [incr y = 1 ifTrue: [4] ifFalse: [0]] ifFalse: [incr x = 1 ifTrue: [2] ifFalse: [6]]. (map isLocationFree: pt) ifFalse: [self halt]. belt _ FactorioPipeToGround new map: map. belt addMaterial: itemName; direction: (dir + 4 \\ 8); location: pt. map addEntity: belt. belt _ FactorioPipeToGround new map: map. belt addMaterial: itemName; direction: (dir \\ 8); location: inputEnd. map addEntity: belt. ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 06:32'! addPowerPoles map addPowerPoles! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 10:04'! buildIndex: k " self new test1 MessageTally spyOn: [self new test1] " | goal copy elapsed showit bucket | showit _ false. elapsed _ Time millisecondClockValue - start. [indexTimes size < k] whileTrue: [indexTimes add: {elapsed. counter. ct2. 0}. showit _ true]. bucket _ indexTimes at: k. bucket at: 4 put: (bucket at: 4) + 1. counter _ counter + 1. k > builds size ifTrue: [ maxDepthCount _ maxDepthCount + 1. (variations size < 1 or: [map totalBounds area < variations last totalBounds area]) ifTrue: [ copy _ map veryDeepCopy. indexTimes add: {elapsed. counter. ct2. 'area'. copy totalBounds area}. variations add: copy. "copy draw: 'a=',copy totalBounds area asString. " ]. variations size > 4 ifTrue: [FactorioBuildInterrupted signal ]. elapsed > 30000 ifTrue: [FactorioBuildInterrupted signal ]. k = 2 ifTrue: [FactorioBuildInterrupted signal ]. ^self ]. goal _ builds at: k. (k \\ 5 = 0 and: [showit]) ifTrue: ["map veryDeepCopy draw: 'depth = ',indexTimes size asString,' g=',goal asString"]. goal visitsPlusOne. self perform: goal action with: k. ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 10:03'! buildNear: k | new goal | goal _ builds at: k. new _ goal obj1. new locationsNear: goal obj2 do: [ :r | self incr2. new bounds: r. map addEntity: new. self buildIndex: k+1. map removeEntity: new. ]. ^self ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 10:41'! buildSupplierInserterConsumer: k | goal supplier consumer | goal _ builds at: k. supplier _ goal obj1. consumer _ goal obj2. self locationsFor: supplier to: consumer do: [ :r :gap | self incr2. supplier bounds: r. map addEntity: supplier. goal successPlusOne. gap > 3 ifTrue: [ self halt. "not right now" k < builds size ifTrue: [ "entities _ supplier insertersAndBeltsTo: consumer." ] ] ifFalse: [ supplier insertersAndBeltsTo: consumer thenDo: [ self buildIndex: k+1 ] for: self ]. map removeEntity: supplier. ]. ^self ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:59'! factoryFor: itemName linkedTo: consumer depth: d connections: ccc | supplier recipes r1 fluid | d > 10 ifTrue: [self halt. ^self]. (#('iron-ore' 'copper-ore' 'coal' 'crude-oil' 'water' 'raw-fish' 'sulfuric-acid' 'petroleum-gas' 'light-oil' 'heavy-oil' 'lubricant' 'iron-plate' 'copper-plate' 'steel-plate') includes: itemName) ifTrue: [ map finalConnections add: {consumer. itemName}. ^self ]. recipes _ luaData preferredRecipes. fluid _ luaData isFluid: itemName. r1 _ recipes at: itemName. itemName = 'solid-fuel' ifTrue: ["self halt"]. supplier _ r1 genericFactoryUnit new map: map. consumer ifNotNil: [consumer addSupplier: supplier]. " #('smelting' 'advanced-crafting' 'crafting-with-fluid' 'oil-processing' 'rocket-building' 'chemistry' '???' 'crafting' 'centrifuging')" supplier recipe: r1; level: d. consumer ifNil: [ builds add: ( FactorioBuildRequirement new obj1: supplier; obj2: 0@0; action: #buildNear:; depth: d ) ] ifNotNil: [ builds add: ( FactorioBuildRequirement new obj1: supplier; obj2: consumer; action: (fluid ifTrue: [#buildSupplierPipeConsumer:] ifFalse: [#buildSupplierInserterConsumer:]); depth: d ) "builds add: {supplier. #buildOneAway:. consumer. d}. builds add: {map newInserter. #buildBetween:. {supplier. consumer}. d}." ]. r1 ingredients do: [ :ingr | self factoryFor: ingr second linkedTo: supplier depth: d+1 connections: r1 ingredients size ]! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 09:04'! incr2 | elapsed | ct2 _ ct2 + 1. ct2 \\ 10000 = 1 ifTrue: [ elapsed _ Time millisecondClockValue - start. {ct2. counter. elapsed. variations size. indexTimes size. builds size} asString displayAt: 0@400. World displayWorldSafely. elapsed > 70000 ifTrue: [ map draw: 'too long ',(builds count: [ :e | e successes > 0]) asString,'/',builds size asString. self halt. ] ]. ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 05:51'! initialize luaData _ FactorioLUA LuaData. map _ FactorioMap new. indexTimes _ OrderedCollection new. variations _ OrderedCollection new. maxDepthCount _ ct2 _ counter _ 0. start _ Time millisecondClockValue. runQuiet _ false. super initialize.! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 16:00'! locationsFor: supplier to: consumer do: aBlock | whereToStart bottom left right top add list coords | self makeLevelRectangles: consumer suppliers size. 1 = 2 ifTrue: [ whereToStart _ 1. builds size > 20 ifTrue: [whereToStart _ 2]. consumer suppliers size > 3 ifTrue: [whereToStart _ 3]. whereToStart to: 3 do: [ : gap | map locations: gap awayFrom: consumer for: supplier do: [ :r | aBlock value: r value: gap] ]. ]. coords _ levelRectangles at: consumer level. top _ coords first. left _ coords second. bottom _ coords third. right _ coords fourth. list _ OrderedCollection new. add _ [ :pt | list add: {pt. consumer location dist: pt} ]. top to: bottom do: [ :y | add value: left@y. add value: right@y. ]. left + 1 to: right - 1 do: [ :x | add value: x@top. add value: x@bottom. ]. list _ list sortBy: [ :a :b | a last < b last]. "list _ list shuffled." list do: [ :e | supplier location: e first. (map isLocationFreeFor: supplier) ifTrue: [aBlock value: supplier bounds value: 1] ]. ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/29/2021 08:11'! makeLevelRectangles: lev1Connections | bottom left right top d deltas | levelRectangles ifNotNil: [^self]. levelRectangles _ OrderedCollection new. bottom _ top _ 0. left _ right _ 0. deltas _ {lev1Connections > 3 ifTrue: [6] ifFalse: [4]. 4. 4}. deltas _ #(8 6 6 4). deltas _ #(10 8 8 6 4). deltas _ #(6 6 6 4). 1 to: 20 do: [ :lev | d _ deltas atPin: lev. top _ top - d. left _ left - d. right _ right + d. bottom _ bottom + d. levelRectangles add: {top. left. bottom. right}. map addRectangle: (left@top corner: (right@bottom)+(3@3)) ]. ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 07:17'! map ^map! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 07:11'! runQuiet: x runQuiet _ x! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 20:19'! shortestRoutesFrom: machine to: bounds do: aBlock | dir dist edge pt pt0 pt1 x0 x1 y0 y1 choices extra | choices _ #(top left bottom right) collect: [ :e | dist _ ((machine perform: e) - (bounds perform: e)) abs. {e. dist} ]. extra _ 1. choices _ choices sortBy: [ :a :b | a second < b second]. choices do: [ :ch | edge _ ch first. 0 to: machine width-1 do: [ :step | edge caseOf: { [#top] -> [ x0 _ x1 _ machine left + step. y0 _ bounds top - 1 - extra. y1 _ machine top - 1. dir _ 0 ]. [#bottom] -> [ x0 _ x1 _ machine left + step. y0 _ bounds bottom + extra. y1 _ machine bottom. dir _ 4 ]. [#left] -> [ x0 _ bounds left - 1 - extra. x1 _ machine left-1. y0 _ y1 _ machine top + step. dir _ 6. ]. [#right] -> [ x0 _ bounds right+extra. x1 _ machine right. y0 _ y1 _ machine top + step. dir _ 2 ]. }. pt _ pt1 _ x1@y1. pt0 _ x0@y0. (aBlock value: pt0 value: pt1) = #continue ifFalse: [^self] ]. ]. ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 17:27'! test1: itemName " self new test1 self new test1: 'copper-cable'. self new test1: 'processing-unit'. self new test1: 'advanced-circuit'. self new test1: 'rocket-control-unit'. self new test1: 'low-density-structure'. self new test1: 'flying-robot-frame'. self new test1: 'advanced-circuit'. self new test1: 'satellite'. self new test1: 'rocket-fuel'. self new test1: 'accumulator'. self new test1: 'radar'. self new test1: 'low-density-structure'. self new test1: 'processing-unit'. self new test1: 'solar-panel'. self new test1: 'spidertron'. self new createMasterRequirements: { {'satellite'. 1}}. MessageTally spyOn: [self new test1: 'satellite'.] " | d more newBuilds | FactorioPathfinder resetCounts. builds _ OrderedCollection new. luaData recipesByResult. self factoryFor: itemName linkedTo: nil depth: 1 connections: 1. newBuilds _ OrderedCollection new. d _ 1. [ newBuilds addAll: (more _ builds select: [ :e | e depth = d]). more isEmpty ] whileFalse: [d _ d + 1]. builds _ newBuilds. newBuilds isEmpty ifFalse: [ [self buildIndex: 1] on: FactorioBuildInterrupted do: [ :ex | map _ variations last. self buildFinalConnections. self addPowerPoles. map draw: 'a=',map totalBounds area asString. blueprintString _ map createBlueprintString. ]. ]. runQuiet ifFalse: [self explore] ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 13:00'! test2: itemName " self new test2: 'satellite'. self new test2: 'spidertron'. self new test2: 'effectivity-module-3' " | r1 ch chunks offset w b2 | self halt. luaData _ FactorioLUA LuaData. chunks _ OrderedCollection new. r1 _ self preferredRecipeFrom: (luaData recipesByResult at: itemName). r1 ingredients do: [ :ingr | ch _ self class new. ch runQuiet: true. chunks add: ch. ch test1: ingr second ]. map _ FactorioMap new. offset _ 0. chunks do: [ :c2 | c2 map entities isEmpty ifFalse: [ b2 _ c2 map totalBounds. w _ b2 width. c2 map entities do: [ :e | e location: e location - b2 origin + (offset@0). map addEntity: e. ]. offset _ offset + w + 4. ]. ]. map draw: 'a=',map totalBounds area asString. blueprintString _ map createBlueprintString. self halt. ! ! !FactorioBuilder methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 10:41'! variations ^variations! ! Object subclass: #FactorioCollection instanceVariableNames: 'elements' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 15:33'! add: x elements add: x! ! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 15:34'! at: x ^(elements detect: [ :e | e key = x]) value! ! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 15:37'! at: x ifAbsent: aBlock ^(elements detect: [ :e | e key = x] ifNone: [^aBlock value]) value! ! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/12/2021 06:01'! at: x ifPresent: block1 ifAbsent: aBlock block1 value: (elements detect: [ :e | e key = x] ifNone: [^aBlock value]) value! ! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 15:36'! do: aBlock elements do: aBlock! ! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 15:39'! first ^elements first! ! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 15:33'! initialize super initialize. elements _ OrderedCollection new.! ! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 19:06'! printOn: s elements printOn: s! ! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 15:39'! second ^elements second! ! !FactorioCollection methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 16:17'! size ^elements size! ! Object subclass: #FactorioInputsAndOutput instanceVariableNames: 'inputs output goal location beltsNeeded isFinalOutput useNearSideInterBelt trueGoal ioLeft ioRight entities primaryEntity widthUsed inputStyle sequence' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/7/2021 17:24'! beltsNeeded ^beltsNeeded! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/7/2021 17:24'! beltsNeeded: x beltsNeeded _ x! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/22/2021 05:42'! entities ^ entities! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/22/2021 05:42'! entities: anObject entities := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/4/2021 08:36'! goal ^ goal! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/4/2021 08:36'! goal: anObject goal := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/22/2021 06:18'! inputStyle ^ inputStyle! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/22/2021 06:18'! inputStyle: anObject inputStyle := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/4/2021 08:32'! inputs ^ inputs! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/4/2021 08:32'! inputs: anObject inputs := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/21/2021 12:53'! ioLeft ^ ioLeft! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/21/2021 12:53'! ioLeft: anObject ioLeft := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/21/2021 12:53'! ioRight ^ ioRight! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/21/2021 12:53'! ioRight: anObject ioRight := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/17/2021 07:08'! isFinalOutput ^ isFinalOutput! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/17/2021 07:08'! isFinalOutput: anObject isFinalOutput := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/7/2021 16:50'! location ^location! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/21/2021 14:31'! location: x location ifNotNil: [self halt]. location _ x. ! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/4/2021 08:32'! output ^ output! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/4/2021 08:32'! output: anObject output := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/23/2021 15:34'! primaryEntity ^ primaryEntity! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/23/2021 15:34'! primaryEntity: anObject primaryEntity := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/22/2021 06:41'! sequence ^ sequence! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/22/2021 06:41'! sequence: anObject sequence := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/20/2021 10:21'! trueGoal ^ trueGoal! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/20/2021 10:21'! trueGoal: anObject trueGoal := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/18/2021 11:19'! useNearSideInterBelt ^ useNearSideInterBelt! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/18/2021 11:19'! useNearSideInterBelt: anObject useNearSideInterBelt := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/22/2021 05:48'! widthUsed ^ widthUsed! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/22/2021 05:48'! widthUsed: anObject widthUsed := anObject.! ! !FactorioInputsAndOutput methodsFor: 'accessing' stamp: 'raa 5/7/2021 16:50'! x ^location x! ! !FactorioInputsAndOutput methodsFor: 'as yet unclassified' stamp: 'raa 5/25/2021 09:37'! addToMap: map entities do: [ :e | e location isString ifTrue: [ map rememberToAdd: e near: entities first location ] ifFalse: [ e location: e location + location. map addEntity: e ]. ]. ! ! !FactorioInputsAndOutput methodsFor: 'as yet unclassified' stamp: 'raa 5/21/2021 14:31'! initialize super initialize. ! ! !FactorioInputsAndOutput methodsFor: 'as yet unclassified' stamp: 'raa 5/21/2021 12:49'! neighborAt: dir do: aBlock self halt.! ! !FactorioInputsAndOutput methodsFor: 'as yet unclassified' stamp: 'raa 5/23/2021 15:37'! outputTimeFor: ignored ^primaryEntity recipe craftingSecondsFor: output qty: 1 at: #modded! ! !FactorioInputsAndOutput methodsFor: 'as yet unclassified' stamp: 'raa 5/21/2021 12:14'! printOn: aStream super printOn: aStream. aStream nextPutAll: ' [',goal asString,'] '; print: output.! ! Object subclass: #FactorioLUA instanceVariableNames: 'stream whites skips keysSeen recipesByName recipesByResult recipesBySize expression' classVariableNames: 'LuaData PreferredRecipes' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 16:33'! cleanLiteral: x x isString ifFalse: [^x]. x first = $* ifTrue: [^x allButFirst]. ^x! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 08:57'! convertRawRecipe: rawRecipe | asmTime details ingr inq rc realThing rnq recipeName | realThing _ FactorioRecipe new. recipeName _ rawRecipe key reject: [ :chx | '["]' includes: chx ]. realThing recipeName: recipeName. details _ rawRecipe value at: 'normal' ifAbsent: [rawRecipe]. ingr _ details value at: 'ingredients'. asmTime _ (details value at: 'energy_required' ifAbsent: [nil->0.5]) value. realThing craftingTime: asmTime; category: (rawRecipe value at: 'category' ifAbsent: ['']). rawRecipe value at: 'results' ifPresent: [ :foo | foo do: [ :eee | rnq _ self ingredientNameAndQuantityFrom: eee value. realThing results add: { rnq second. rnq first}. (recipesByResult at: rnq first ifAbsentPut: [OrderedCollection new]) add: realThing ]. ] ifAbsent: [ rc _ rawRecipe value at: 'result_count' ifAbsent: [1]. realThing results add: {rc value. recipeName}. (recipesByResult at: (details value at: 'result') ifAbsentPut: [OrderedCollection new] ) add: realThing ]. realThing ingredients: OrderedCollection new. ingr do: [ :ix | inq _ self ingredientNameAndQuantityFrom: ix value. realThing ingredients add: { inq second. inq first}. ]. recipesByName at: recipeName put: realThing. (recipesBySize at: realThing ingredients size ifAbsentPut: [OrderedCollection new]) add: realThing. ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 14:57'! debug | pos intro | pos _ stream position. intro _ 'size = ',stream size asStringWithCommas,' pos = ',pos asStringWithCommas,String cr. stream position: (pos - 500 max: 0). (intro,(stream next: pos - stream position),'<===>',(stream next: 500)) bobEdit. stream position: pos. self halt.! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 15:33'! getArray | first answer elem nilKeys | nilKeys _ 0. answer _ FactorioCollection new. [ first _ self getToken. first = ',' ifTrue: [first _ self getToken.]. first = '}' ifTrue: [ "(nilKeys = 0 or: [nilKeys = answer size]) ifFalse: [self debug]." ^answer ]. answer add: (elem _ self getArrayElement: first). (elem key isNil and: elem value = '=' ) ifTrue: [self debug]. elem key = nil ifTrue: [nilKeys _ nilKeys + 1]. ] repeat. ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 19:43'! getArrayElement: arg | equal first key | first _ arg. first isNumber ifTrue: [ ^nil -> first]. first first =$* ifTrue: [^nil -> first allButFirst]. first = 'nil' ifTrue: [^nil -> nil]. (first = '{') ifTrue: [ ^nil -> self getArray ]. key _ first. keysSeen add: key. equal _ self getToken. equal = '=' ifFalse: [self debug]. first _ self getToken. "key = 'time' ifTrue: [self debug]." first = '{' ifTrue: [ ^key -> self getArray ] ifFalse: [ ^key -> (self cleanLiteral: first) ]. ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 12:26'! getExpression | first | first _ self getToken. first = '{' ifTrue: [^self getArray]. self halt.! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 14:43'! getToken | answer ch loops | loops _ 0. answer _ nil. [ ch _ stream peek ifNil: [^answer]. (loops _ loops + 1) > 200 ifTrue: [self halt]. self isWhite: ch ] whileTrue: [ stream next ]. ch isLetter ifTrue: [^self parseIdentifier]. ch = $[ ifTrue: [^self parseBracketThing]. ch =$@ ifTrue: [^self parseAtThingy]. (#(${ $= $} $,) includes: ch) ifTrue: [ ^String with: stream next. ]. ch = $" ifTrue: [^self parseStringLiteral]. ('0123456789+-.' includes: ch) ifTrue: [^self parseNumber]. self debug. ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 16:18'! ingredientNameAndQuantityFrom: x x size = 2 ifTrue: [^{x first value. x second value}]. ^{x at: 'name'. x at: 'amount'}! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 09:58'! isFluid: itemName (expression at: 'fluid') at: itemName ifAbsent: [^false]. ^true! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 10:45'! isWhite: ch whites ifNil: [whites _ {Character space. Character tab. Character cr. Character lf}]. ^whites includes: ch! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 09:50'! parse: fn " self new parse: '/Users/bob/Desktop/rawFactorio.txt' " | firstFew rawRecipes | recipesByName _ Dictionary new. recipesByResult _ Dictionary new. recipesBySize _ Dictionary new. keysSeen _ Bag new. skips _ OrderedCollection new. stream _ StandardFileStream readOnlyFileNamed: fn. firstFew _ OrderedCollection new. self getToken. self getToken. expression _ self getExpression. rawRecipes _ expression at: 'recipe'. rawRecipes do: [ :r1 | self convertRawRecipe: r1 ]. {'recipesByName'. recipesByName. 'recipesByResult'. recipesByResult. 'expr'. expression. 'recipesBySize'. recipesBySize} explore halt. self testRecipes ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 14:33'! parseAtThingy | answer ch loops | loops _ 0. answer _ String new writeStream. [ ch _ stream peek ifNil: [^answer contents]. self isWhite: ch ] whileFalse: [ answer nextPut: stream next. (loops _ loops + 1) > 200 ifTrue: [self halt]. ]. ^answer contents ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 16:27'! parseBracketThing | answer brackets ch loops clean | loops _ 0. answer _ String new writeStream. brackets _ 0. [ ch _ stream next. answer nextPut: ch. ch = $[ ifTrue: [brackets _ brackets + 1]. ch = $] ifTrue: [brackets _ brackets - 1]. brackets > 0 ] whileTrue. answer _ answer contents. clean _ answer reject: [ :e | '["]' includes: e]. clean size = (answer size - 4) ifTrue: [^clean]. "e.g. [0]" ^answer ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 14:31'! parseIdentifier | answer ch loops | loops _ 0. answer _ String new writeStream. [ ch _ stream peek ifNil: [^answer contents]. ch isAlphaNumeric or: [ch = $_] ] whileTrue: [ answer nextPut: stream next. (loops _ loops + 1) > 100 ifTrue: [self halt]. ]. ^answer contents ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 14:45'! parseNumber | answer brackets ch loops temp | loops _ 0. answer _ String new writeStream. [ ch _ stream peek. '0123456789+-.e/' includes: ch ] whileTrue: [ answer nextPut: stream next. (loops _ loops + 1) > 100 ifTrue: [self halt]. ]. temp _ answer contents. temp = '--' ifTrue: [ answer _ String new writeStream. brackets _ 0. [ ch _ stream next. answer nextPut: ch. ch = $[ ifTrue: [brackets _ brackets + 1]. ch = $] ifTrue: [brackets _ brackets - 1]. brackets > 0 ] whileTrue. skips add: answer contents. ^self getToken ]. ^temp asNumber ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 14:53'! parseStringLiteral | answer ch loops | stream next. loops _ 0. answer _ String new writeStream. [ ch _ stream next. ch = $" ifTrue: [ stream peek = $" ifTrue: [ answer nextPut: stream next ] ifFalse: [ ^'*',answer contents ] ] ifFalse: [ ch = $\ ifTrue: [ answer nextPut: stream next ] ifFalse: [ answer nextPut: ch ] ]. (loops _ loops + 1) > 10000 ifTrue: [self halt]. ] repeat. ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 09:50'! readRecipes | firstFew rawRecipes fn | fn _ '/Users/bob/Desktop/rawFactorio.txt'. recipesByName _ Dictionary new. recipesByResult _ Dictionary new. recipesBySize _ Dictionary new. keysSeen _ Bag new. skips _ OrderedCollection new. stream _ StandardFileStream readOnlyFileNamed: fn. firstFew _ OrderedCollection new. self getToken. self getToken. expression _ self getExpression. rawRecipes _ expression at: 'recipe'. rawRecipes do: [ :r1 | self convertRawRecipe: r1 ]. "{'recipesByName'. recipesByName. 'recipesByResult'. recipesByResult. 'expr'. expr} explore halt. self testRecipes" ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 5/15/2021 13:31'! recipesByName ^recipesByName! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 07:57'! recipesByResult ^recipesByResult! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 08:37'! testRecipes | out rdata resultQty requirements input rqmt stoppers need iter intermediate raw qty time timeList | out _ String new writeStream. requirements _ Dictionary new. raw _ Dictionary new. intermediate _ Dictionary new. stoppers _ #('copper-plate' 'iron-plate' 'stone' 'coal' 'sulfur' 'lubricant' 'sulfuric-acid' 'plastic-bar'). #('utility-science-pack' 'automation-science-pack' 'military-science-pack' 'logistic-science-pack' 'chemical-science-pack' 'production-science-pack') do: [ :sp | requirements at: sp put: 100. "intermediate at: sp put: 100." ]. iter _ 0. [requirements isEmpty] whileFalse: [ iter _ iter + 1. out cr; nextPutAll: 'iteration ',iter asString; cr; cr. input _ requirements. requirements _ Dictionary new. input keysDo: [ : ik | rqmt _ input at: ik. ((stoppers includes: ik) or: [(recipesByResult includesKey: ik) not]) ifTrue: [ raw at: ik add: rqmt. out nextPutAll: ik,' (raw) ',rqmt asString; cr. ] ifFalse: [ intermediate at: ik add: rqmt. out nextPutAll: ik,' >> ',rqmt asString; cr. rdata _ (recipesByResult at: ik) first. resultQty _ rdata resultCountFor: ik. rdata ingredients do: [ :tuple |":qty :name" need _ (tuple first * rqmt / resultQty) rounded. out tab; nextPutAll: tuple second,' >> ',need asString; cr. requirements at: tuple second add: need. ]. ]. ]. ]. timeList _ OrderedCollection new. intermediate keys asArray sorted do: [ :k | rdata _ (recipesByResult at: k) first. qty _ intermediate at: k. time _ qty / (rdata resultCountFor: k) * rdata craftingTime. timeList add: {k. qty. time}. ]. out cr; cr; nextPutAll: 'item'; tab; nextPutAll: 'qty'; tab; nextPutAll: 'time'; cr. timeList _ timeList sortBy: [ :a :b | a third > b third]. timeList do: [ :e | e do: [ :ee | out nextPutAll: ee asString; tab]. out cr]. out contents bobEdit. {'raw'. raw. 'intermediate'. intermediate. 'keysSeen'. keysSeen sortedElements} explore ! ! !FactorioLUA methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 15:56'! updateRecipe: realThing | list | realThing results do: [ :e | list _ recipesByResult at: e second ifAbsentPut: [OrderedCollection new]. list removeAllSuchThat: [ :rr | rr recipeName = realThing recipeName]. list add: realThing. ]. recipesByName at: realThing recipeName put: realThing. list _ recipesBySize at: realThing ingredients size ifAbsentPut: [OrderedCollection new]. list removeAllSuchThat: [ :rr | rr recipeName = realThing recipeName]. list add: realThing. ! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! FactorioLUA class instanceVariableNames: ''! !FactorioLUA class methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 14:34'! LuaData ^LuaData ifNil: [LuaData _ self new readRecipes]! ! !FactorioLUA class methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:53'! preferredRecipeFrom: aList aList size = 1 ifTrue: [^aList first]. aList do: [ :e | e ingredients ? 'barrel' ifFalse: [^e] ]. ^aList first! ! !FactorioLUA class methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:53'! preferredRecipes PreferredRecipes ifNil: [ PreferredRecipes _ Dictionary new. self LuaData recipesByResult keysAndValuesDo: [ :k :v | PreferredRecipes at: k put: (self preferredRecipeFrom: v) ]. ]. ^PreferredRecipes! ! Object subclass: #FactorioMap instanceVariableNames: 'entities debug tilesInUse finalConnections rectangles recentChanges lastBig lastScale lastOffset lastDraw deferredEntities poleDebug mapName rawInputs' classVariableNames: 'CachedGraphics' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/25/2021 09:34'! add: thing near: loc self locationsNear: loc for: thing do: [ : rect | thing location: rect origin. self addEntity: thing. ^self ]! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/25/2021 09:37'! addDeferred deferredEntities do: [ :tuple | " {thing. loc}" self add: tuple first near: tuple second ]! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 06:59'! addEntity: x (self entityIntersecting: x) ifNotNilDo: [ :other | other do: [ :oo | oo = self ifTrue: [self halt] ]. other explore halt ]. entities add: x. x locationsDo: [ :pt | tilesInUse add: pt]. self recentChange: {#add. x class. x bounds} ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 07:09'! addPowerPoles | needy pole target miniReach pt area bestCount bestPt loops newPower otherPoles prevPole k possibles bingo originalBoundsCenterY | loops _ 0. otherPoles _ OrderedCollection new. needy _ self entities select: [ :e | e requiresPower]. originalBoundsCenterY _ self totalBounds center y. [needy isEmpty] whileFalse: [ (loops _ loops + 1) > 10000 ifTrue: [self halt]. pole _ self newPowerPole. miniReach _ pole supplyArea x // 2. k _ 1. [ target _ needy at: k. bestPt _ bestCount _ nil. possibles _ OrderedCollection new. target left - miniReach to: target right + miniReach do: [ :x | target top - miniReach to: target bottom + miniReach do: [ :y | pt _ x@y. pole location: pt. ((self isLocationFreeFor: pole) and: [otherPoles isEmpty or: [otherPoles anySatisfy: [ :op | (op location dist: pt) <= pole wireReach]]] ) ifTrue: [ area _ pt - (miniReach @ miniReach) extent: pole supplyArea. newPower _ needy select: [ :nn | nn bounds intersects: area]. newPower isEmpty ifFalse: [ possibles add: {pt. newPower. (originalBoundsCenterY - pt y) abs} ]. ]. ] ]. possibles isEmpty ifTrue: [ "Transcript show: 'add power fail ',k asString,' ',loops asString; cr." "failed to connect" ] ifFalse: [ possibles _ possibles sortBy: [ :a :b | a second size > b second size or: [a second size = b second size and: [a third < b third]] ]. "possibles explore halt." bingo _ possibles first. "bingo _ possibles detect: [ :e | e third] ifNone: [first]. bingo second size < second size ifTrue: [self halt]." bestPt _ bingo first. bestCount _ bingo second. pole location: bestPt. prevPole _ otherPoles detect: [ :op | (op location dist: bestPt) <= pole wireReach] ifNone: [nil]. pole circuit: 1 color: 'green' to: prevPole circuit: 1. pole circuit: 1 color: 'red' to: prevPole circuit: 1. self addEntity: pole. otherPoles add: pole. needy removeAll: bestCount. "poleDebug add: {pole. possibles. bestPt. bestCount}. bestPt = (67@13) ifTrue: [self halt]." ]. bestPt isNil and: [k < needy size] ] whileTrue: [k _ k + 1]. bestPt ifNil: [^self addPowerPoles2: needy]. ]. ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 07:10'! addPowerPoles2: needy | pole1 target miniReach pt area bestCount bestPt loops newPower otherPoles prevPole k pole2 secondLoc | loops _ 0. otherPoles _ entities select: [ :e | e isPowerPole]. "otherPoles isEmpty or: [otherPoles anySatisfy: [ :op | (op location dist: pt) <= 9]]" [needy isEmpty] whileFalse: [ (loops _ loops + 1) > 10000 ifTrue: [self halt]. pole1 _ self newPowerPole. pole2 _ self newPowerPole. miniReach _ pole1 supplyArea x // 2. k _ 1. [ target _ needy at: k. bestPt _ bestCount _ nil. target left - miniReach to: target right + miniReach do: [ :x | target top - miniReach to: target bottom + miniReach do: [ :y | pt _ x@y. pole1 location: pt. (self isLocationFreeFor: pole1) ifTrue: [ area _ pt - (miniReach @ miniReach) extent: pole1 supplyArea. newPower _ needy select: [ :nn | nn bounds intersects: area]. newPower isEmpty ifFalse: [ (bestPt isNil or: [newPower size > bestCount size]) ifTrue: [ bestPt _ pt. bestCount _ newPower. ]. ]. ]. ] ]. bestPt ifNil: [ self halt. Transcript show: 'add power fail ',k asString,' ',loops asString; cr. "failed to connect" ] ifNotNil: [ pole1 location: bestPt. prevPole _ otherPoles detectMin: [ :op | op location dist: bestPt ]. prevPole ifNil: [self halt]. secondLoc _ self pointNear: (bestPt + prevPole location // 2) suchThat: [ :p2 | p2 ~= bestPt and: [(p2 dist: bestPt) <= pole1 wireReach] and: [(p2 dist: prevPole location) <= pole1 wireReach] and: [pole2 location: p2. self isLocationFree: pole2] ]. secondLoc ifNil: [self halt] ifNotNil: [ pole2 location: secondLoc. pole2 circuit: 1 color: 'green' to: prevPole circuit: 1. pole2 circuit: 1 color: 'red' to: prevPole circuit: 1. pole1 circuit: 1 color: 'green' to: pole2 circuit: 1. pole1 circuit: 1 color: 'red' to: pole2 circuit: 1. self addEntity: pole1; addEntity: pole2. otherPoles add: pole1; add: pole2. ]. needy removeAll: bestCount. ]. bestPt isNil and: [k < needy size] ] whileTrue: [k _ k + 1]. bestPt ifNil: [ Transcript show: 'power poles incomplete ',needy asString; cr. ^self ]. ]. ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 14:36'! addRectangle: r (rectangles includes: r) ifFalse: [rectangles add: r].! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/29/2021 14:46'! animateRecent | r2 scaledR m temps | temps _ Dictionary new. recentChanges do: [ :rh | r2 _ rh third. scaledR _ r2 origin - lastBig origin * lastScale + lastOffset extent: r2 extent * lastScale. rh first = #add ifTrue: [ m _ Morph new color: Color gray; bounds: scaledR. lastDraw addMorph: m. temps at: scaledR put: m. ] ifFalse: [ temps at: scaledR ifPresent: [ :m2 | temps removeKey: scaledR. lastDraw removeMorph: m2. ]. ]. World displayWorldSafely. (Delay forMilliseconds: 100) wait. ].! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/14/2021 06:51'! createBlueprint " FactorioBuilder new test1 " ^self createBlueprintString bobEdit "(FactorioTests decode: encodedString allButFirst)" "inspect" ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 06:22'! createBlueprintString ^FactorioTests encode: self createBlueprintStringContents asByteArray. ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 06:26'! createBlueprintStringContents " FactorioBuilder new test1 " | out | entities withIndexDo: [ :e :k | e entityID: k]. out _ FactorioOutputStream new. out nextPutAll: '{"blueprint":{'. out nextPutAll: '"icons":[{"signal":{"type":"item","name":"assembling-machine-3"},"index":1}],'. out key: 'entities' andCollection: entities. out comma; key: 'item' andValue: 'blueprint'; comma; key: 'label' andValue: mapName. out nextPutAll: '}}'. "or" ',"item":"blueprint","label":"numberOne","version":281479273906176},"index":0}'. "out contents bobEdit." ^out contents ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/29/2021 14:28'! draw: extraText " FactorioBuilder new test1 " | big f offset scale r different fullIcon resize icon2 bg im icon3 bgColor missing scaledR | big _ self totalBounds. scale _ 20. offset _ 10@10. extraText ifNotNil: [offset _ 20@20]. f _ Morph new color: Color lightYellow; borderStyle: (SimpleBorder new width: 2; color: Color gray); extent: big extent * scale + offset + offset. f setProperty: #ScaleParameters toValue: {big. scale. offset}. lastDraw _ f. lastBig _ big. lastScale _ scale. lastOffset _ offset. extraText ifNotNil: [f addMorph: (extraText asString asMorph position: 2@2)]. rectangles do: [ :r2 | scaledR _ r2 origin - big origin * scale + offset extent: r2 extent * scale. scaledR _ scaledR intersect: f bounds. scaledR = f bounds ifFalse: [ f addMorph: (Morph new bounds: scaledR; color: (Color gray alpha: 0.15)) ]. ]. entities do: [ :e | r _ e location - big origin * scale + offset extent: e extent * scale. fullIcon _ e entityGraphic. resize _ (e bounds width * scale / fullIcon width) min: (e bounds height * scale / fullIcon height). different _ fullIcon magnify: fullIcon boundingBox by: resize smoothing: 2. icon2 _ self iconFor: {e overlayName. 64@64}. icon2 ifNotNil: [ icon3 _ icon2 magnifyBy: different width * 0.5 / icon2 width. different getCanvas translucentImage: icon3 at: different extent - icon3 extent // 2. ]. missing _ e suppliers count: [ :sup | (entities includes: sup) not]. bgColor _ e = entities last ifTrue: [ Color cyan ] ifFalse: [ Color r: 0 g: (1.1 - (e level * 0.1) min: 1.0 max: 0.1) b: 0 ]. missing > 0 ifTrue: [bgColor _ Color r: (missing * 0.08 + 0.5 min: 1.0) g: 0 b: 0]. bg _ Morph new extent: r extent; position: r origin; setProperty: #MapEntity toValue: e. e showBorder ifTrue: [ bg color: bgColor; borderStyle: (SimpleBorder new width: 1; color: Color veryLightGray) ] ifFalse: [ bg color: Color transparent ]. im _ ImageMorph new "borderStyle: (SimpleBorder new width: 1; color: Color veryLightGray);" image: different; balloonText: e asString; center: bg center. bg addMorph: im. f addMorph: bg. ]. f asMorph openInWorld! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 06:40'! entities ^entities! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 09:46'! entity: e isRawInputFor: items rawInputs add: {e. items}! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 06:58'! entityIntersecting: newGuy | newRect | newGuy locationsDo: [ :loc | (tilesInUse occurrencesOf: loc) > 0 ifTrue: [ newRect _ newGuy bounds. ^(entities select: [ :e | e intersects: newRect]) ] ]. ^nil ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 17:53'! finalConnections ^finalConnections! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 17:44'! graphicFor: x | dir form ex fn r | x ifNil: [^(StringMorph contents: '?') imageForm]. fn _ x first. ex _ x second. r _ ex isPoint ifTrue: [0@0 extent: ex] ifFalse: [ex]. dir _ '/Users/bob/Library/Application Support/Steam/steamapps/common/Factorio/factorio.app/Contents/data/base/graphics/'. CachedGraphics ifNil: [CachedGraphics _ Dictionary new]. form _ CachedGraphics at: fn ifAbsentPut: [ ImageReadWriter formFromStream: (FileStream oldFileNamed: dir,fn) ]. "(form copy: (0@0 extent: 64@64)) asMorph openInWorld halt." ^form copy: r " /Users/bob/Library/Application Support/Steam/steamapps/common/Factorio/factorio.app/Contents/data/base/graphics/entity/assembling-machine-3/assembling-machine-3.png "! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/29/2021 06:21'! iconFor: x " self new iconFor: {'icons/inserter.png'. 64@64} " | dir form ex fn fullName | x ifNil: [^(StringMorph contents: '?') imageForm]. fn _ x first. fn ifNil: [^nil]. fn = 'nil' ifTrue: [^nil]. ex _ x second. dir _ '/Users/bob/Library/Application Support/Steam/steamapps/common/Factorio/factorio.app/Contents/data/base/graphics/icons/'. form _ CachedGraphics at: fn ifAbsentPut: [ fullName _ dir,fn,'.png'. (FileDirectory default fileExists: fullName) ifFalse: [ fullName _ dir,'fluid/',fn,'.png'. (FileDirectory default fileExists: fullName) ifFalse: [self halt. ^nil]. ]. ImageReadWriter formFromStream: (FileStream oldFileNamed: fullName) ]. "(form copy: (0@0 extent: 64@64)) asMorph openInWorld halt." ^form copy: (0@0 extent: ex) " /Users/bob/Library/Application Support/Steam/steamapps/common/Factorio/factorio.app/Contents/data/base/graphics/entity/assembling-machine-3/assembling-machine-3.png "! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 09:46'! initialize super initialize. entities _ OrderedCollection new. deferredEntities _ OrderedCollection new. debug _ OrderedCollection new. finalConnections _ OrderedCollection new. tilesInUse _ Bag new. rectangles _ OrderedCollection new. recentChanges _ OrderedCollection new. poleDebug _ OrderedCollection new. rawInputs _ OrderedCollection new.! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 10:07'! isLocation: pt freeFor: newGuy | newRect | self halt. newRect _ pt extent: newGuy extent. ^(entities anySatisfy: [ :e | e intersects: newRect]) not ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 07:15'! isLocationFree: loc ^ (tilesInUse occurrencesOf: loc) = 0! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 06:58'! isLocationFreeFor: newGuy "| newRect | newRect _ newGuy bounds. ^(entities anySatisfy: [ :e | e intersects: newRect]) not" newGuy locationsDo: [ :loc | (tilesInUse occurrencesOf: loc) > 0 ifTrue: [^false]]. ^true ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 07:14'! locations: dist awayFrom: place for: newGuy do: aBlock | list add | list _ OrderedCollection new. add _ [ :pt | list add: {pt. 0@0 dist: pt} ]. place top - newGuy height + 1 to: place bottom - 1 do: [ :y | add value: (place left - newGuy width - dist)@y. add value: (place right + dist)@y. ]. place left - newGuy width + 1 to: place right - 1 do: [ :x | add value: x@(place top - newGuy height - dist). add value: x@(place bottom + dist). ]. list _ list sortBy: [ :a :b | a last > b last]. "list _ list shuffled." list do: [ :e | newGuy location: e first. (self isLocationFreeFor: newGuy) ifTrue: [aBlock value: newGuy bounds] ]. ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 16:13'! locationsNear: place for: newGuy do: aBlock | bottom top left right ct list | 0 to: 10 do: [ :dist | left _ place x - dist. right _ place x + dist. top _ place y - dist. bottom _ place y + dist. list _ OrderedCollection new. ct _ 0. top + 1 to: bottom - 1 do: [ :y | list add: {left. y. ct_ct + 3}. list add: {right. y. ct_ct + 3}. ]. ct _ 1. left to: right do: [ :x | list add: {x. top. ct_ct + 3}. list add: {x. bottom. ct_ct + 3}. ]. list _ list sortBy: [ :a :b | a last < b last]. "list _ list shuffled." list do: [ :e | newGuy location: e first@e second. (self isLocationFreeFor: newGuy) ifTrue: [aBlock value: newGuy bounds] ]. ]! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 14:35'! luaData ^FactorioLUA LuaData! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 06:57'! mapName ^mapName! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 06:57'! mapName: x mapName _ x! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 11:27'! newAssembler ^FactorioAssembler new map: self! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 11:37'! newInserter ^FactorioInserter new map: self! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/23/2021 15:37'! newInserter: secondsPerUse secondsPerUse < 1 ifTrue: [^FactorioStackInserter new map: self]. ^FactorioInserter new map: self! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 07:20'! newPowerPole ^(true ifTrue: [FactorioPowerPole] ifFalse: [FactorioSubstation]) new map: self! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/22/2021 07:10'! pointNear: pt suchThat: aBlock 0 to: 10 do: [ :j | 0-j to: j do: [ :x | 0-j to: j do: [ :y | (aBlock value: pt + (x@y)) ifTrue: [^pt + (x@y)] ]. ]. ]. ^nil! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 16:53'! pruneTo: stackLevel [entities size > stackLevel] whileTrue: [ self removeEntity: entities last ]! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/29/2021 14:24'! recentChange: x recentChanges add: x. recentChanges size > 1000 ifTrue: [recentChanges removeFirst]. ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/25/2021 09:37'! rememberToAdd: thing near: loc deferredEntities add: {thing. loc}! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 06:59'! removeEntity: x entities remove: x. x locationsDo: [ :pt | tilesInUse remove: pt]. self recentChange: {#remove. x class. x bounds} ! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 16:52'! stackLevel ^entities size! ! !FactorioMap methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 05:53'! totalBounds | big | big _ nil. entities do: [ :e | big ifNil: [big _ e bounds]. big _ big merge: e bounds. ]. ^big! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! FactorioMap class instanceVariableNames: ''! !FactorioMap class methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 11:05'! betterArrow2: aColor " TTCFont betterArrow2 " | ex f t1 t2 y right x1 x2 | x1 _ 50. x2 _ 34. t1 _ 8. t2 _ 34. right _ 128. ex _ right@120. f _ Form extent: ex depth: 32. "f fillColor: Color white." y _ 52. f getCanvas drawPolygon: {0@y. x1@(y-t2). x2@(y-t1). right@(y-t1). right@(y+t1). x2@(y+t1). x1@(y+t2). 0@y} color: aColor borderWidth: 0 borderColor: aColor. ^f magnifyBy: 0.15! ! !FactorioMap class methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 11:13'! betterArrow3: aColor " FactorioMap betterArrow3: Color red " | ex f t1 t2 y right x1 x2 | x1 _ 50. x2 _ 50. t1 _ 16. t2 _ 34. right _ 128. y _ 52. ex _ right@(y*2). f _ Form extent: ex depth: 32. "f fillColor: Color white." f getCanvas drawPolygon: {0@y. x1@(y-t2). x2@(y-t1). right@(y-t1). right@(y+t1). x2@(y+t1). x1@(y+t2). 0@y} color: aColor borderWidth: 0 borderColor: aColor. "f asMorph openInWorld" "magnifyBy: 0.15" ^f! ! Object subclass: #FactorioMapEntity instanceVariableNames: 'map location extent direction connections suppliers level entityID filters explicitControlBehavior requestFilters isRawInputFor' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/21/2021 20:02'! bottom ^location y + extent y! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/20/2021 11:30'! bounds: r location _ r origin. extent _ r extent! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/22/2021 19:38'! connections ^connections! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/23/2021 19:33'! direction ^ direction! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/23/2021 19:33'! direction: anObject direction := anObject.! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/30/2021 17:09'! entityID ^entityID! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/30/2021 17:10'! entityID: x entityID _ x! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 5/1/2021 06:32'! explicitControlBehavior: x explicitControlBehavior _ x! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/20/2021 09:39'! extent ^ extent! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/20/2021 09:39'! extent: anObject extent := anObject.! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/21/2021 20:00'! height ^extent y! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 5/30/2021 09:48'! isRawInputFor ^isRawInputFor! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/21/2021 20:03'! left ^location x! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/26/2021 08:21'! level ^level! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/26/2021 08:21'! level: x level _ x ! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/20/2021 09:39'! location ^ location! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 5/25/2021 09:22'! location: anObject location := anObject. anObject isString ifTrue: [^self]. location x > 9000 ifTrue: [self halt].! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 5/28/2021 07:00'! locations | answer | self halt. answer _ OrderedCollection new. 0 to: extent x - 1 do: [ :x | 0 to: extent y - 1 do: [ :y | answer add: x@y+self location ] ]. ^answer! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 5/28/2021 06:57'! locationsDo: aBlock 0 to: extent x - 1 do: [ :x | 0 to: extent y - 1 do: [ :y | aBlock value: x@y+self location ] ]. ! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/20/2021 11:27'! map ^ map! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/20/2021 11:27'! map: anObject map := anObject.! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/21/2021 20:03'! right ^location x + extent x! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/23/2021 06:57'! suppliers ^suppliers! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/21/2021 20:02'! top ^location y! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/21/2021 20:00'! width ^extent x! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/20/2021 09:44'! x ^ location x! ! !FactorioMapEntity methodsFor: 'accessing' stamp: 'raa 4/20/2021 09:44'! y ^ location y! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 09:01'! addConnection: inOut color: conColor to: entity circuit: circuit ((connections at: inOut ifAbsentPut: [Dictionary new]) at: conColor ifAbsentPut: [OrderedCollection new]) add: {entity. circuit}! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/1/2021 06:36'! addFilter: itemName qty: qty filters add: {itemName. qty} ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 07:12'! addRequestFilter: itemName qty: qty requestFilters ifNil: [requestFilters _ OrderedCollection new]. requestFilters add: {itemName. qty} ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 06:55'! addSupplier: x suppliers add: x! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 11:52'! adjacentTo: other | me them | me _ self bounds. them _ other bounds. me left = them right ifTrue: [ me top >= them bottom ifTrue: [^nil]. them top >= me bottom ifTrue: [^nil]. ^#left ]. me right = them left ifTrue: [ me top >= them bottom ifTrue: [^nil]. them top >= me bottom ifTrue: [^nil]. ^#right ]. me top = them bottom ifTrue: [ me left >= them right ifTrue: [^nil]. them left >= me right ifTrue: [^nil]. ^#top ]. me bottom = them top ifTrue: [ me left >= them right ifTrue: [^nil]. them left >= me right ifTrue: [^nil]. ^#bottom ]. ^nil! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/20/2021 21:56'! bar: ignored! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 07:35'! blueprintOn: s s key: 'entity_number' andNumber: entityID; comma; key: 'name' andValue: self simpleName; comma. self positionOn: s. direction ifNotNil: [ s comma; key: 'direction' andNumber: direction . ]. self connectionsOn: s. self controlBehaviorOn: s. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 09:45'! bounds ^location extent: extent! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/9/2021 16:00'! circuit: inOut color: conColor to: other circuit: circuit other ifNil: [^self]. self addConnection: inOut color: conColor to: other circuit: circuit. other addConnection: circuit color: conColor to: self circuit: inOut. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/11/2021 15:14'! connectedEntities | answer | answer _ Set new. connections do: [ : byCircuit | byCircuit do: [ : byColor | byColor do: [ : tuple | answer add: tuple first. ]. ]. ]. ^answer ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 19:09'! connectionDebugString | s | s _ String new writeStream. self connectionsDo: [ :io :color :ent | s nextPutAll: ent asString,' dist=',(self dist: ent) asString; cr. ]. ^s contents! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 19:07'! connectionsDo: aBlock | d | connections keys do: [ : kInOut | (d _ connections at: kInOut) keys asArray sorted do: [ : kcolor | (d at: kcolor) do: [ :tuple | aBlock value: kInOut value: kcolor value: tuple first ] ]. ]. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 15:30'! connectionsOn: s | d | connections isEmpty ifFalse: [ s comma; quote: 'connections'; colon; brace: [ connections keys do: [ : kInOut | s quote: kInOut; colon; brace: [ (d _ connections at: kInOut) keys asArray sorted do: [ : kcolor | s quote: kcolor; colon; bracket: [ (d at: kcolor) do: [ :tuple | s brace: [ s key: 'entity_id' andNumber: tuple first entityID. tuple second ifNotNil: [ s comma. s key: 'circuit_id' andNumber: tuple second. ] ] ] separatedBy: [s comma] ] ] separatedBy: [s comma]. ]. ] separatedBy: [s comma]. ] ]. "'connections':{'1':{'red':[{'entity_id':18}]},'2':{'red':[{'entity_id':21}]}"! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 07:18'! controlBehaviorOn: s explicitControlBehavior ifNotNil: [ s comma; nextPutAll: explicitControlBehavior. ^self ]. self requestFiltersOn: s. self plainFiltersOn: s.! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 19:45'! directionFrom: aTile ^direction _ (self directionTo: aTile) + 4 \\ 8! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/11/2021 11:32'! dist: other ^self location dist: other location! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 14:53'! entityGraphic ^map graphicFor: self entityGraphicName.! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 20:21'! entityGraphicName ^nil! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/14/2021 17:38'! findClearSpot | start | start _ self location. 0 to: 10 do: [ :n | 0-n to: n do: [ :dx | 0-n to: n do: [ :dy | self location: start + (dx@dy). (map isLocationFreeFor: self ) ifTrue: [^self] ] ] ]. self halt.! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 08:05'! findPathsTo: other ^FactorioPathfinder new findBeltPathFrom: self to: other ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 06:20'! flipInsertersNorthToSouth! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 15:40'! iconName ^nil! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 20:51'! initialize suppliers _ OrderedCollection new. connections _ Dictionary new. level _ direction _ 0. super initialize. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 19:09'! inserter: inserter spotTo: other | n pt bingo | n _ pt _ nil. self halt. bingo _ [ :newDir | ^inserter gap: n; direction: newDir; location: pt ]. self left > other right ifTrue: [ (self top max: other top) to: (self bottom min: other bottom) - 1 do: [ : y | n _ self left - other right. 1 to: n do: [ : i | pt _ other right + i - 1@y. (map isLocationFree: pt) ifTrue: [ (n < 3 or: [i = 2]) ifTrue: [ bingo value: 2 ]. ] ]. ]. ^nil ]. self right < other left ifTrue: [ (self top max: other top) to: (self bottom min: other bottom) - 1 do: [ : y | n _ other left - self right. 1 to: n do: [ : i | pt _ self right + i - 1@y. (map isLocationFree: pt) ifTrue: [ (n < 3 or: [i = 2]) ifTrue: [ bingo value: 6 ] ] ]. ]. ^nil ]. self top > other bottom ifTrue: [ (self left max: other left) to: (self right min: other right) - 1 do: [ : x | n _ self top - other bottom. 1 to: n do: [ : i | pt _ x@(other bottom + i - 1). (map isLocationFree: pt) ifTrue: [ (n < 3 or: [i = 2]) ifTrue: [ bingo value: 4 ] ] ]. ]. ^nil ]. self bottom < other top ifTrue: [ (self left max: other left) to: (self right min: other right) - 1 do: [ : x | n _ other top - self bottom. 1 to: n do: [ : i | pt _ x@(self bottom + i - 1). (map isLocationFree: pt) ifTrue: [ (n < 3 or: [i = 2]) ifTrue: [ bingo value: 0 ] ] ]. ]. ^nil ]. self halt. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 07:18'! inserterSpotTo: other self halt. self left > other right ifTrue: [self halt]. self right < other left ifTrue: [ self top to: self bottom - 1 do: [ : y | self right to: other left - 1 do: [ :x | (map isLocationFree: x@y) ifTrue: [^x@y] ]. ]. self halt ]. self top > other bottom ifTrue: [self halt]. self bottom < other top ifTrue: [self halt]. self halt. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 16:56'! insertersAndBeltsTo: consumer | n pt bingo inserter before | self halt. inserter _ n _ pt _ nil. before _ map stackLevel. map draw: 'insertersAndBeltsTo:'. self halt. bingo _ [ :newDir | " inserter _ map newInserter. inserter supplier: supplier; consumer: consumer. map addEntity: inserter. self buildIndex: k+1. map removeEntity: inserter. " self halt. ^inserter gap: n; direction: newDir; location: pt ]. self left > consumer right ifTrue: [ (self top max: consumer top) to: (self bottom min: consumer bottom) - 1 do: [ : y | n _ self left - consumer right. 1 to: n do: [ : i | pt _ consumer right + i - 1@y. (map isLocationFree: pt) ifTrue: [ (n < 3 or: [i = 2]) ifTrue: [ bingo value: 2 ]. ] ]. ]. ^nil ]. self right < consumer left ifTrue: [ (self top max: consumer top) to: (self bottom min: consumer bottom) - 1 do: [ : y | n _ consumer left - self right. 1 to: n do: [ : i | pt _ self right + i - 1@y. (map isLocationFree: pt) ifTrue: [ (n < 3 or: [i = 2]) ifTrue: [ bingo value: 6 ] ] ]. ]. ^nil ]. self top > consumer bottom ifTrue: [ (self left max: consumer left) to: (self right min: consumer right) - 1 do: [ : x | n _ self top - consumer bottom. 1 to: n do: [ : i | pt _ x@(consumer bottom + i - 1). (map isLocationFree: pt) ifTrue: [ (n < 3 or: [i = 2]) ifTrue: [ bingo value: 4 ] ] ]. ]. ^nil ]. self bottom < consumer top ifTrue: [ (self left max: consumer left) to: (self right min: consumer right) - 1 do: [ : x | n _ consumer top - self bottom. 1 to: n do: [ : i | pt _ x@(self bottom + i - 1). (map isLocationFree: pt) ifTrue: [ (n < 3 or: [i = 2]) ifTrue: [ bingo value: 0 ] ] ]. ]. ^nil ]. self halt. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 19:24'! insertersAndBeltsTo: consumer thenDo: aBlock for: builder | gap bingo before spots makeInserter makeBelt bb | gap _ nil. makeInserter _ [ :where :newDir | map newInserter supplier: self; consumer: consumer; gap: gap; direction: newDir; location: where yourself ]. makeBelt _ [ :where :newDir | FactorioBelt new map: map; direction: newDir + 4 \\ 8; location: where yourself ]. bingo _ [ :newDir :rawSpots | spots _ rawSpots collect: [ :e |(map isLocationFree: e) ifTrue: [e] ifFalse: [nil]]. before _ map stackLevel. spots size <= 2 ifTrue: [ spots do: [ :ss | ss ifNotNil: [ map addEntity: (makeInserter value: ss value: newDir). aBlock value. map pruneTo: before. ] ]. ]. spots size = 3 ifTrue: [ (spots includes: nil) ifTrue: [ (spots at: 2) ifNotNil: [ map addEntity: (makeInserter value: (spots at: 2) value: newDir). aBlock value. map pruneTo: before. ] ] ifFalse: [ map addEntity: ((makeInserter value: (spots at: 1) value: newDir) gap: 1). map addEntity: ((makeInserter value: (spots at: 3) value: newDir) gap: 1). map addEntity: (makeBelt value: (spots at: 2) value: newDir). aBlock value. map pruneTo: before. ]. ]. spots size > 3 ifTrue: [ (spots includes: nil) ifTrue: [ "harder to do" ] ifFalse: [ map addEntity: ((makeInserter value: (spots at: 1) value: newDir) gap: 1). map addEntity: ((makeInserter value: (spots at: spots size) value: newDir) gap: 1). 2 to: spots size - 1 do: [ :sx | bb _ makeBelt value: (spots at: sx) value: newDir. bb purpose: 'from ',self asString,' to ',consumer asString. map addEntity: bb. ]. aBlock value. map pruneTo: before. ]. ]. map pruneTo: before. ]. self left > consumer right ifTrue: [ (self top max: consumer top) to: (self bottom min: consumer bottom) - 1 do: [ : y | gap _ self left - consumer right. bingo value: 2 value: ((1 to: gap) collect: [ : i | consumer right + i - 1@y]) ]. self makeComplexPathTo: consumer thenDo: aBlock for: builder. ^nil ]. self right < consumer left ifTrue: [ (self top max: consumer top) to: (self bottom min: consumer bottom) - 1 do: [ : y | gap _ consumer left - self right. bingo value: 6 value: ((1 to: gap) collect: [ : i | self right + i - 1@y ]) ]. self makeComplexPathTo: consumer thenDo: aBlock for: builder. ^nil ]. self top > consumer bottom ifTrue: [ (self left max: consumer left) to: (self right min: consumer right) - 1 do: [ : x | gap _ self top - consumer bottom. bingo value: 4 value: ((1 to: gap) collect: [ : i | x@(consumer bottom + i - 1) ]) ]. self makeComplexPathTo: consumer thenDo: aBlock for: builder. ^nil ]. self bottom < consumer top ifTrue: [ (self left max: consumer left) to: (self right min: consumer right) - 1 do: [ : x | gap _ consumer top - self bottom. bingo value: 0 value: ((1 to: gap) collect: [ : i | x@(self bottom + i - 1)]) ]. self makeComplexPathTo: consumer thenDo: aBlock for: builder. ^nil ]. self halt. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 09:45'! intersects: x ^self bounds intersects: x! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/8/2021 10:08'! isAssembler ^false! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/9/2021 16:04'! isPowerPole ^false! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 09:48'! isRawInputFor: items isRawInputFor _ items! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/16/2021 08:13'! isSmelter ^false! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 20:19'! isTransport ^false! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 14:32'! landingZones | answer | answer _ OrderedCollection new. self top to: self bottom - 1 do: [ :y | answer add: (self left-2)@y; add: (self right+1)@y ]. self left to: self right - 1 do: [ :x | answer add: x@(self top-2); add: x@(self bottom+1) ]. ^answer! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 17:00'! landingZones2 | answer | answer _ OrderedCollection new. self top to: self bottom - 1 do: [ :y | answer add: {(self left-1)@y. (self left-2)@y}; add: {(self right)@y. (self right+1)@y} ]. self left to: self right - 1 do: [ :x | answer add: {x@(self top-1). x@(self top-2)}; add: {x@(self bottom). x@(self bottom+1)} ]. ^answer! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 11:29'! locationsNear: pt do: aBlock map locationsNear: pt for: self do: aBlock ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/29/2021 05:49'! makeComplexPathTo: consumer thenDo: aBlock for: builder | bb before v | (self findPathsTo: consumer) do: [ :path1 | before _ map stackLevel. v _ builder variations size. (bb _ FactorioInserter new) map: map; gap: 1; location: (path1 at: 1); directionTo: (path1 at: 2); purpose: 'from ',self asString,' to ',consumer asString. map addEntity: bb. (bb _ FactorioInserter new) map: map; gap: 1; location: (path1 at: path1 size); directionFrom: (path1 at: path1 size - 1); purpose: 'from ',self asString,' to ',consumer asString. map addEntity: bb. 2 to: path1 size - 1 do: [ : sx | (bb _ FactorioBelt new) map: map; location: (path1 at: sx); directionTo: (path1 at: sx+1); purpose: 'from ',self asString,' to ',consumer asString. map addEntity: bb. ]. aBlock value. v = builder variations size ifFalse: ["self halt"]. map pruneTo: before. ]. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 10:47'! overlayName ^nil! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 07:18'! plainFiltersOn: s filters ifNil: [^self]. filters isEmpty ifTrue: [^self]. s comma; key: 'control_behavior' andBrace: [ s key: 'filters' andBracket: [ filters withIndexDo: [ :f :index | index = 1 ifFalse: [s comma]. s brace: [ s key: 'signal' andBrace: [ s key: 'type' andValue: ((map luaData isFluid: f first) ifTrue: ['fluid'] ifFalse: ['item']); comma; key: 'name' andValue: f first ]; comma; key: 'count' andNumber: f second; comma; key: 'index' andNumber: index. ]. ]. ]. ]. " {'entity_number':1,'name':'constant-combinator','position':{'x':-241.5,'y':1089.5},'control_behavior':{'filters':[{'signal':{'type':'item','name':'iron-plate'},'count':1,'index':1}]}} "! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 11:49'! positionOn: s | grid | grid _ 0.0. "0.5" s nextPutAll: '"position":{'; nextPutAll: '"x":',(self bounds center x + grid) asString; nextPut: $,; nextPutAll: '"y":',(self bounds center y + grid) asString; nextPut: $}! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/11/2021 11:28'! printOn: s s nextPutAll: self simpleName,' d=',direction asString,' loc=',self location asString. ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 19:30'! purpose: ignored! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/11/2021 15:19'! removeConnectionsTo: other connections do: [ : byCircuit | byCircuit do: [ : byColor | byColor do: [ : tuple | other = tuple first ifTrue: [tuple at: 1 put: nil] ]. ]. ]. self removeEmptyConnections.! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/11/2021 15:25'! removeEmptyConnections | d | connections keys do: [ : kInOut | (d _ connections at: kInOut) keys do: [ : kcolor | (d at: kcolor) removeAllSuchThat: [ :f | f first isNil]. (d at: kcolor) removeAllSuchThat: [ :f | f first entityID isNil]. (d at: kcolor) isEmpty ifTrue: [d removeKey: kcolor] ]. d isEmpty ifTrue: [connections removeKey: kInOut] ] ! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 07:17'! requestFiltersOn: s requestFilters ifNil: [^self]. requestFilters isEmpty ifTrue: [^self]. s comma. s key: 'request_filters' andBracket: [ requestFilters withIndexDo: [ :f :index | index = 1 ifFalse: [s comma]. s brace: [ s key: 'name' andValue: f first; comma; key: 'count' andNumber: f second; comma; key: 'index' andNumber: index. ]. ]. ]. " {'entity_number':1,'name':'constant-combinator','position':{'x':-241.5,'y':1089.5},'control_behavior':{'filters':[{'signal':{'type':'item','name':'iron-plate'},'count':1,'index':1}]}} "! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 05:49'! requiresPower ^false! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:24'! showBorder ^true! ! !FactorioMapEntity methodsFor: 'as yet unclassified' stamp: 'raa 5/16/2021 08:26'! testAmbiguousInputs: x ^false! ! FactorioMapEntity subclass: #FactorioArithmeticCombinator instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioArithmeticCombinator methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 07:35'! blueprintOn: s super blueprintOn: s. " {'entity_number':1,'name':'constant-combinator','position':{'x':-241.5,'y':1089.5},'control_behavior':{'filters':[{'signal':{'type':'item','name':'iron-plate'},'count':1,'index':1}]}} "! ! !FactorioArithmeticCombinator methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 11:43'! direction: d direction _ d. d \\ 4 = 0 ifTrue: [^self extent: 1@2]. self extent: 2@1.! ! !FactorioArithmeticCombinator methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 08:12'! entityGraphic | f | f _ Form extent: 32@32 depth: 32. f fillColor: Color magenta. ^f! ! !FactorioArithmeticCombinator methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 11:31'! initialize self extent: 2@1. filters _ OrderedCollection new. super initialize. ! ! !FactorioArithmeticCombinator methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 06:27'! requiresPower ^true! ! !FactorioArithmeticCombinator methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 20:37'! simpleName ^'arithmetic-combinator'! ! FactorioMapEntity subclass: #FactorioBigElectricPole instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioBigElectricPole methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 06:28'! entityGraphic | f | f _ Form extent: 32@32 depth: 32. f fillColor: Color orange. ^f! ! !FactorioBigElectricPole methodsFor: 'as yet unclassified' stamp: 'raa 5/15/2021 09:47'! initialize self extent: 2@2. super initialize. ! ! !FactorioBigElectricPole methodsFor: 'as yet unclassified' stamp: 'raa 5/9/2021 16:04'! isPowerPole ^true! ! !FactorioBigElectricPole methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:24'! showBorder ^false! ! !FactorioBigElectricPole methodsFor: 'as yet unclassified' stamp: 'raa 5/15/2021 09:48'! simpleName ^'big-electric-pole'! ! !FactorioBigElectricPole methodsFor: 'as yet unclassified' stamp: 'raa 5/15/2021 09:48'! supplyArea ^4@4! ! !FactorioBigElectricPole methodsFor: 'as yet unclassified' stamp: 'raa 5/15/2021 09:48'! wireReach ^30! ! FactorioMapEntity subclass: #FactorioChest instanceVariableNames: 'bar' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioChest methodsFor: 'as yet unclassified' stamp: 'raa 5/5/2021 09:02'! blueprintOn: s super blueprintOn: s. bar ifNotNil: [ s comma; key: 'bar' andNumber: bar ] ! ! !FactorioChest methodsFor: 'accessing' stamp: 'raa 5/5/2021 09:02'! bar ^ bar! ! !FactorioChest methodsFor: 'accessing' stamp: 'raa 5/5/2021 09:02'! bar: anObject bar := anObject.! ! FactorioChest subclass: #FactorioActiveProviderChest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioActiveProviderChest methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 19:44'! initialize self extent: 1@1. super initialize. ! ! !FactorioActiveProviderChest methodsFor: 'as yet unclassified' stamp: 'raa 5/15/2021 13:20'! simpleName ^'logistic-chest-active-provider'! ! FactorioMapEntity subclass: #FactorioConstantCombinator instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioConstantCombinator methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 07:35'! blueprintOn: s super blueprintOn: s. " {'entity_number':1,'name':'constant-combinator','position':{'x':-241.5,'y':1089.5},'control_behavior':{'filters':[{'signal':{'type':'item','name':'iron-plate'},'count':1,'index':1}]}} "! ! !FactorioConstantCombinator methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 08:12'! entityGraphic | f | f _ Form extent: 32@32 depth: 32. f fillColor: Color magenta. ^f! ! !FactorioConstantCombinator methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 12:31'! initialize self extent: 1@1. filters _ OrderedCollection new. super initialize. ! ! !FactorioConstantCombinator methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 08:10'! simpleName ^'constant-combinator'! ! FactorioMapEntity subclass: #FactorioDeciderCombinator instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioDeciderCombinator methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 11:43'! direction: d direction _ d. d \\ 4 = 0 ifTrue: [^self extent: 1@2]. self extent: 2@1.! ! !FactorioDeciderCombinator methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 08:12'! entityGraphic | f | f _ Form extent: 32@32 depth: 32. f fillColor: Color magenta. ^f! ! !FactorioDeciderCombinator methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 11:31'! initialize self extent: 2@1. filters _ OrderedCollection new. super initialize. ! ! !FactorioDeciderCombinator methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 06:27'! requiresPower ^true! ! !FactorioDeciderCombinator methodsFor: 'as yet unclassified' stamp: 'raa 5/17/2021 12:39'! simpleName ^'decider-combinator'! ! FactorioMapEntity subclass: #FactorioInserter instanceVariableNames: 'supplier consumer gap bar' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 5/5/2021 09:03'! blueprintOn: s super blueprintOn: s. bar ifNotNil: [ s comma; key: 'bar' andNumber: bar ] ! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 19:41'! directionTo: nextTile nextTile ifNil: [^direction _ 0]. self x = nextTile x ifTrue: [ ^direction _ self y > nextTile y ifTrue: [4] ifFalse: [0] ]. ^direction _ self x > nextTile x ifTrue: [2] ifFalse: [6] ! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:57'! entityGraphic | g | g _ FactorioMap betterArrow3: (gap = 1 ifTrue: [Color blue] ifFalse: [Color red]). direction = 6 ifTrue: [^g rotateBy: 180]. direction = 4 ifTrue: [^g rotateBy: 90]. direction = 0 ifTrue: [^g rotateBy: 270]. ^g! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 20:27'! entityGraphicName ^{'icons/inserter.png'. 64@64}! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 06:21'! flipInsertersNorthToSouth direction = 0 ifTrue: [^direction _ 4]. direction = 4 ifTrue: [^direction _ 0]. ! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 20:17'! iconName ^'icons/inserter.png'! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 5/16/2021 08:39'! initialize self extent: 1@1. filters _ OrderedCollection new. super initialize. ! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 5/16/2021 08:45'! plainFiltersOn: s filters ifNil: [^self]. filters isEmpty ifTrue: [^self]. s comma; key: 'filters' andBracket: [ filters withIndexDo: [ :f :index | index = 1 ifFalse: [s comma]. s brace: [ s key: 'name' andValue: f first; comma; key: 'index' andNumber: index. ]. ]. ]. " {'entity_number':1,'name':'filter-inserter','position':{'x':-180.5,'y':2385.5},'filters':[{'index':1,'name':'copper-plate'}]} "! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 09:00'! printOn: s super printOn: s. s nextPutAll: ' from ',supplier asString,' to ',consumer asString! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 05:49'! requiresPower ^true! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 10:20'! showBorder ^false! ! !FactorioInserter methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 20:27'! simpleName ^gap = 1 ifTrue: ['fast-inserter'] ifFalse: ['long-handed-inserter']! ! !FactorioInserter methodsFor: 'accessing' stamp: 'raa 5/4/2021 12:19'! bar ^ bar! ! !FactorioInserter methodsFor: 'accessing' stamp: 'raa 5/4/2021 12:19'! bar: anObject bar := anObject.! ! !FactorioInserter methodsFor: 'accessing' stamp: 'raa 4/22/2021 08:24'! consumer ^ consumer! ! !FactorioInserter methodsFor: 'accessing' stamp: 'raa 4/22/2021 08:24'! consumer: anObject consumer := anObject.! ! !FactorioInserter methodsFor: 'accessing' stamp: 'raa 4/22/2021 09:03'! gap ^ gap! ! !FactorioInserter methodsFor: 'accessing' stamp: 'raa 4/22/2021 09:03'! gap: anObject gap := anObject.! ! !FactorioInserter methodsFor: 'accessing' stamp: 'raa 4/22/2021 08:24'! supplier ^ supplier! ! !FactorioInserter methodsFor: 'accessing' stamp: 'raa 4/22/2021 08:24'! supplier: anObject supplier := anObject.! ! FactorioInserter subclass: #FactorioFilterInserter instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioFilterInserter methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 20:37'! simpleName ^'filter-inserter'! ! FactorioMapEntity subclass: #FactorioMaker instanceVariableNames: 'recipe items' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioMaker methodsFor: 'accessing' stamp: 'raa 4/22/2021 08:55'! recipe ^ recipe! ! !FactorioMaker methodsFor: 'accessing' stamp: 'raa 4/22/2021 08:55'! recipe: anObject recipe := anObject.! ! !FactorioMaker methodsFor: 'as yet unclassified' stamp: 'raa 5/4/2021 06:05'! addItems: x items ifNil: [items _ OrderedCollection new]. items add: x.! ! !FactorioMaker methodsFor: 'as yet unclassified' stamp: 'raa 5/4/2021 06:04'! blueprintOn: s super blueprintOn: s. recipe ifNotNil: [ s comma; key: 'recipe' andValue: recipe recipeName. ]. items ifNotNil: [ s comma; key: 'items' andBrace: [ items do: [ :e | s key: e first andNumber: e second. ] separatedBy: [s comma] ] ]. ! ! !FactorioMaker methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 19:49'! printOn: s super printOn: s. recipe ifNotNil: [s nextPutAll: ' [',recipe recipeName asString,']']! ! !FactorioMaker methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 05:49'! requiresPower ^true! ! FactorioMaker subclass: #FactorioAssembler instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioAssembler methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 16:41'! defaultInputOffset direction = 0 ifTrue: [^1@(-1)]. direction = 4 ifTrue: [^1@(3)]. self halt.! ! !FactorioAssembler methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 20:26'! entityGraphicName ^{'entity/assembling-machine-3/assembling-machine-3.png'. 119@119}! ! !FactorioAssembler methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 20:16'! iconName ^'icons/assembling-machine-3.png'! ! !FactorioAssembler methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 09:38'! initialize self extent: 3@3. super initialize. ! ! !FactorioAssembler methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 14:55'! inputOutputSides direction = 0 ifTrue: [^#(top bottom)]. direction = 2 ifTrue: [^#(right left)]. direction = 4 ifTrue: [^#(bottom top)]. direction = 6 ifTrue: [^#(left right)]. ! ! !FactorioAssembler methodsFor: 'as yet unclassified' stamp: 'raa 5/8/2021 10:07'! isAssembler ^true! ! !FactorioAssembler methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 20:48'! isFluidInput: pt direction = 6 ifTrue: [^pt = (location + ((-1)@1))]. direction = 4 ifTrue: [^pt = (location + (1@extent y))]. direction = 2 ifTrue: [^pt = (location + (extent x@1))]. direction = 0 ifTrue: [^pt = (location + (1@(-1)))]. self halt. ^true ! ! !FactorioAssembler methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 14:42'! overlayName ^recipe recipeName! ! !FactorioAssembler methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 12:46'! simpleName ^'assembling-machine-3'! ! FactorioMaker subclass: #FactorioChemicalPlant instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioChemicalPlant methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 18:24'! defaultInputOffset direction = 0 ifTrue: [^0@(-1)]. direction = 4 ifTrue: [^2@(3)]. self halt! ! !FactorioChemicalPlant methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 15:29'! entityGraphicName ^{'entity/chemical-plant/chemical-plant.png'. 108@148} ! ! !FactorioChemicalPlant methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 09:38'! initialize self extent: 3@3. super initialize. ! ! !FactorioChemicalPlant methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 14:55'! inputOutputSides direction = 0 ifTrue: [^#(top bottom)]. direction = 2 ifTrue: [^#(right left)]. direction = 4 ifTrue: [^#(bottom top)]. direction = 6 ifTrue: [^#(left right)]. ! ! !FactorioChemicalPlant methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 20:44'! isFluidInput: pt direction = 6 ifTrue: [^(pt = (location - (1@0))) or: [pt = (location - (1@0) + (0@2))]]. direction = 4 ifTrue: [^(pt = (location+ (0@extent y))) or: [pt = (location + (2@extent y)) ]]. direction = 2 ifTrue: [^(pt = (location + (3@0))) or: [pt = (location + (3@2))]]. direction = 0 ifTrue: [^(pt = (location+ (0@(-1)))) or: [pt = (location + (2@(-1))) ]]. self halt. ^true ! ! !FactorioChemicalPlant methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 14:39'! outputInputOrder " output then input clockwise " #( ('heavy-oil-cracking' ('light-oil' 'light-oil' 'water' 'heavy-oil')) ('sulfuric-acid' ('sulfuric-acid' 'sulfuric-acid' 'water' 'water')) ('light-oil-petroleum-gas' ('petroleum-gas' 'petroleum-gas' 'water' 'light-oil')) ('lubricant' ('lubricant' 'lubricant' 'heavy-oil' 'heavy-oil')) ('light-oil-solid-fuel' ('' '' 'light-oil' 'light-oil' )) ('petroleum-gas-solid-fuel' ('' '' 'petroleum-gas' 'petroleum-gas' )) ('heavy-oil-solid-fuel' ('' '' 'heavy-oil' 'heavy-oil' )) ('plastic' ('' '' 'petroleum-gas' 'petroleum-gas')) ('sulfur' ('' '' 'water' 'petroleum-gas')) ('battery' ('' '' 'sulfuric-acid' 'sulfuric-acid')) ('explosive' ('' '' 'water' 'water')) )! ! !FactorioChemicalPlant methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 14:42'! overlayName ^recipe recipeName! ! !FactorioChemicalPlant methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 19:20'! simpleName ^'chemical-plant'! ! Object subclass: #FactorioMiddleBeltBoundaries instanceVariableNames: 'sections' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioMiddleBeltBoundaries methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 11:50'! add: x sections add: x ! ! !FactorioMiddleBeltBoundaries methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 11:50'! initialize super initialize. sections _ OrderedCollection new.! ! !FactorioMiddleBeltBoundaries methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 11:55'! totalQty ^sections detectSum: [ :e | e itemQty]! ! !FactorioMiddleBeltBoundaries methodsFor: 'accessing' stamp: 'raa 5/28/2021 12:00'! sections ^ sections! ! !FactorioMiddleBeltBoundaries methodsFor: 'accessing' stamp: 'raa 5/28/2021 12:00'! sections: anObject sections := anObject.! ! Object subclass: #FactorioMiddleBeltSection instanceVariableNames: 'item itemQty positions machines plusMinus cumulativeAbsolute cumulativeRaw' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioMiddleBeltSection methodsFor: 'accessing' stamp: 'raa 5/28/2021 11:46'! item ^ item! ! !FactorioMiddleBeltSection methodsFor: 'accessing' stamp: 'raa 5/28/2021 11:46'! item: anObject item := anObject.! ! !FactorioMiddleBeltSection methodsFor: 'accessing' stamp: 'raa 5/28/2021 11:53'! itemQty ^ itemQty! ! !FactorioMiddleBeltSection methodsFor: 'accessing' stamp: 'raa 5/28/2021 11:53'! itemQty: anObject itemQty := anObject.! ! !FactorioMiddleBeltSection methodsFor: 'accessing' stamp: 'raa 5/28/2021 12:20'! machines ^ machines! ! !FactorioMiddleBeltSection methodsFor: 'accessing' stamp: 'raa 5/28/2021 12:20'! machines: anObject machines := anObject.! ! !FactorioMiddleBeltSection methodsFor: 'accessing' stamp: 'raa 5/28/2021 11:46'! positions ^ positions! ! !FactorioMiddleBeltSection methodsFor: 'accessing' stamp: 'raa 5/28/2021 11:46'! positions: anObject positions := anObject.! ! !FactorioMiddleBeltSection methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 14:04'! bottleneck self cumulativeQuantities. ^cumulativeAbsolute max! ! !FactorioMiddleBeltSection methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 14:10'! cumulativeQuantities | recipes itemsPerSecond rdata cyclesPerSecond cum | cumulativeRaw ifNotNil: [^self]. recipes _ FactorioLUA preferredRecipes. plusMinus _ OrderedCollection new. machines do: [ :m | m ifNil: [ plusMinus add: 15 "raw input" ] ifNotNil: [ rdata _ recipes at: m output. cyclesPerSecond _ rdata moddedMultiplier / rdata craftingTime. m output = item ifTrue: [ itemsPerSecond _ (rdata resultCountFor: item) * cyclesPerSecond. plusMinus add: itemsPerSecond. ] ifFalse: [ rdata ingredients do: [ :ii | ii second = item ifTrue: [ itemsPerSecond _ ii first * cyclesPerSecond. plusMinus add: itemsPerSecond negated. ] ]. ] ]. ]. cum _ 0. cumulativeAbsolute _ plusMinus collect: [ :e | cum _ cum + e max: 0 ]. cum _ 0. cumulativeRaw _ plusMinus collect: [ :e | cum _ cum + e ]. ^cumulativeAbsolute! ! !FactorioMiddleBeltSection methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:03'! firstPosition ^positions first! ! !FactorioMiddleBeltSection methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:03'! lastPosition ^positions last! ! !FactorioMiddleBeltSection methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 14:07'! onOffBottleneck self cumulativeQuantities. ^{ (plusMinus select: [ :e | e > 0]) sum rounded. (plusMinus select: [ :e | e < 0]) sum rounded. cumulativeAbsolute max } asArray! ! FactorioMaker subclass: #FactorioOilRefinery instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioOilRefinery methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 15:33'! entityGraphicName ^{'entity/oil-refinery/hr-oil-refinery.png'. 386@430} ! ! !FactorioOilRefinery methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 15:34'! initialize self extent: 5@5. super initialize. ! ! !FactorioOilRefinery methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 14:43'! overlayName ^recipe recipeName! ! !FactorioOilRefinery methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 12:47'! simpleName ^'oil-refinery'! ! Object subclass: #FactorioOutputStream instanceVariableNames: 'stream indent' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 13:00'! brace: aBlock self openBrace. indent _ indent + 1. self cr. aBlock value. indent _ indent - 1. self cr. self closeBrace.! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 17:20'! bracket: aBlock self openBracket. aBlock value. self closeBracket.! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 17:01'! closeBrace self nextPut: $}! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 17:01'! closeBracket self nextPut: $]! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 16:20'! colon stream nextPutAll: ':'! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 13:01'! comma stream nextPutAll: ','. self cr.! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 17:15'! contents ^stream contents! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 13:00'! cr stream cr. indent timesRepeat: [stream tab]! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 12:59'! initialize super initialize. stream _ String new writeStream. indent _ 0.! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 5/1/2021 06:24'! key: k andBrace: aBlock self quote: k; colon; brace: aBlock! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 5/1/2021 06:25'! key: k andBracket: aBlock self quote: k; colon; bracket: aBlock! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 17:23'! key: k andCollection: v self quote: k; colon; bracket: [ v do: [ :e | self brace: [self print: e]] separatedBy: [self comma]. ]. ! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 17:24'! key: k andCollection: v with: printer self quote: k; colon; bracket: [ v do: [ :e | self brace: [printer value: e]. ] separatedBy: [self comma]. ] ! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 5/2/2021 07:42'! key: k andNumber: v self quote: k; colon; nextPutAll: v asString! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 16:32'! key: k andValue: v self quote: k; colon; quote: v ! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 16:32'! nextPut: ch stream nextPut: ch! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 16:33'! nextPutAll: x stream nextPutAll: x! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 17:00'! openBrace self nextPut: ${! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 17:01'! openBracket self nextPut: $[! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 16:40'! print: x x blueprintOn: self! ! !FactorioOutputStream methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 16:19'! quote: x stream nextPutAll: '"',x asString,'"'! ! FactorioChest subclass: #FactorioPassiveProviderChest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioPassiveProviderChest methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 19:44'! initialize self extent: 1@1. super initialize. ! ! !FactorioPassiveProviderChest methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 19:26'! simpleName ^'logistic-chest-passive-provider'! ! Object subclass: #FactorioPathfinder instanceVariableNames: 'visited end1 end2 currentPath bestPath limit map' classVariableNames: 'TotalMS TotalPaths TotalUses' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioPathfinder methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 17:36'! check1: foobar | done curr lev next hits allRoutes link thisRoute | allRoutes _ OrderedCollection new. end2 do: [ :theEnd | done _ Dictionary new. done at: theEnd first put: 'stopper'. next _ end1 collect: [ :e | {e second. nil}]. lev _ 1. [ curr _ next. next _ Set new. curr do: [ :e | (map isLocationFree: e first) ifTrue: [ done at: e first ifAbsentPut: [ self neighborsOf: e first do: [ :f | next add: {f. e first} ]. e ] ] ]. hits _ next select: [ :nn | theEnd second = nn first]. hits isEmpty ifFalse: [ hits do: [ :hh | thisRoute _ OrderedCollection new. allRoutes add: thisRoute. thisRoute add: theEnd first. "inserter #2" link _ hh. [ thisRoute addFirst: link first. link second isNil ] whileFalse: [ link _ done at: link second ]. thisRoute addFirst: (end1 detect: [ :e1 | e1 second = thisRoute first]) first. "inserter #1" ]. lev _ 999. ]. lev < 8 ] whileTrue: [ lev _ lev + 1 ]. ]. ^allRoutes! ! !FactorioPathfinder methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 17:02'! findBeltPathFrom: origin to: destination | t | TotalMS ifNil: [ TotalMS _ TotalPaths _ TotalUses _ 0. ]. TotalUses _ TotalUses + 1. map _ origin map. bestPath _ nil. end1 _ origin landingZones2 sortBy: [ :a :b | (a second dist: destination location) < (b second dist: destination location)]. end2 _ destination landingZones2 sortBy: [ :a :b | (a second dist: origin location) < (b second dist: origin location)]. end1 _ (end1 first: 7) select: [ :e | (map isLocationFree: e second) and: [map isLocationFree: e first]]. end2 _ (end2 first: 7) select: [ :e | (map isLocationFree: e second) and: [map isLocationFree: e first]]. 1 = 1 ifTrue: [^self check1: nil]. #landingZones2 halt. limit _ 4. [ t _ [end1 do: [ :start | currentPath _ OrderedCollection new. self probe: start. ]] timeToRun. TotalMS _ TotalMS + t. "'limit=',limit asString,' ',t asString,' ms' displayAt: 0@250." bestPath isNil and: [t < 100] ] whileTrue: [ limit _ limit + 1. ]. bestPath ifNotNil: [TotalPaths _ TotalPaths + 1]. TotalUses \\ 10 = 1 ifTrue: [{TotalMS. TotalUses. TotalPaths} asString displayAt: 0@270]. bestPath ifNil: [^nil]. bestPath isEmpty ifTrue: [^nil]. ^bestPath! ! !FactorioPathfinder methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 07:31'! neighborsOf: tile do: aBlock aBlock value: tile x@(tile y - 1). aBlock value: tile x@(tile y + 1). aBlock value: (tile x - 1)@tile y. aBlock value: (tile x + 1)@tile y. ! ! !FactorioPathfinder methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 10:51'! probe: tile (currentPath includes: tile) ifTrue: [^self]. (map isLocationFree: tile) ifFalse: [^self]. currentPath size > limit ifTrue: [^self]. currentPath add: tile. (end2 includes: tile) ifTrue: [ (bestPath isNil or: [bestPath size > currentPath size]) ifTrue: [ bestPath _ currentPath. "Transcript show: bestPath size asString,'=',bestPath asArray asString; cr; cr." ] ] ifFalse: [ self neighborsOf: tile do: [ :e | self probe: e ]. ]. currentPath remove: tile. ! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! FactorioPathfinder class instanceVariableNames: ''! !FactorioPathfinder class methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 10:50'! resetCounts TotalMS _ TotalPaths _ TotalUses _ 0.! ! FactorioMapEntity subclass: #FactorioPowerPole instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioPowerPole methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 06:28'! entityGraphic | f | f _ Form extent: 32@32 depth: 32. f fillColor: Color orange. ^f! ! !FactorioPowerPole methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 11:57'! initialize self extent: 1@1. super initialize. ! ! !FactorioPowerPole methodsFor: 'as yet unclassified' stamp: 'raa 5/9/2021 16:04'! isPowerPole ^true! ! !FactorioPowerPole methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:24'! showBorder ^false! ! !FactorioPowerPole methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 05:51'! simpleName ^'medium-electric-pole'! ! !FactorioPowerPole methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 05:57'! supplyArea ^7@7! ! !FactorioPowerPole methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 05:57'! wireReach ^9! ! FactorioMapEntity subclass: #FactorioRadar instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioRadar methodsFor: 'as yet unclassified' stamp: 'raa 5/14/2021 11:52'! initialize super initialize. self extent: 3@3.! ! !FactorioRadar methodsFor: 'as yet unclassified' stamp: 'raa 5/14/2021 11:52'! simpleName ^'radar'! ! Object subclass: #FactorioRecipe instanceVariableNames: 'ingredients craftingTime results category recipeName' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/21/2021 15:17'! category ^ category! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/21/2021 15:17'! category: anObject category := anObject.! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 5/23/2021 15:12'! craftingSecondsFor: item qty: qty at: multiplier | factor | factor _ multiplier = #modded ifTrue: [self moddedMultiplier] ifFalse: [multiplier]. ^ qty * craftingTime / factor / (self resultCountFor: item) ! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/11/2021 19:52'! craftingTime ^ craftingTime! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/11/2021 19:52'! craftingTime: anObject craftingTime := anObject.! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/21/2021 20:18'! genericFactoryUnit " #('smelting' 'advanced-crafting' 'crafting-with-fluid' 'oil-processing' 'rocket-building' 'chemistry' '???' 'crafting' 'centrifuging')" category isEmpty ifTrue: [^FactorioAssembler]. category = 'advanced-crafting' ifTrue: [^FactorioAssembler]. category = 'crafting' ifTrue: [^FactorioAssembler]. category = 'crafting-with-fluid' ifTrue: [^FactorioAssembler]. category = 'smelting' ifTrue: [^FactorioSmelter]. category = 'chemistry' ifTrue: [^FactorioChemicalPlant]. category = 'oil-processing' ifTrue: [^FactorioOilRefinery]. self halt.! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/11/2021 19:52'! ingredients ^ ingredients! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/11/2021 19:52'! ingredients: anObject ingredients := anObject.! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 5/7/2021 17:15'! newFactory ^self genericFactoryUnit new recipe: self ! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/22/2021 14:39'! recipeName ^ recipeName! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/22/2021 14:39'! recipeName: anObject recipeName := anObject.! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/20/2021 08:38'! resultCountFor: x results do: [ :e | e second = x ifTrue: [^e first]]. self halt.! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/20/2021 08:13'! results ^results ifNil: [results _ OrderedCollection new]! ! !FactorioRecipe methodsFor: 'accessing' stamp: 'raa 4/21/2021 15:17'! results: anObject results := anObject.! ! !FactorioRecipe methodsFor: 'as yet unclassified' stamp: 'raa 5/23/2021 15:11'! moddedMultiplier | gfu | gfu _ self genericFactoryUnit. gfu = FactorioAssembler ifTrue: [^3.75]. gfu = FactorioSmelter ifTrue: [^4]. gfu = FactorioChemicalPlant ifTrue: [^2.5]. self halt.! ! !FactorioRecipe methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 08:59'! printOn: s super printOn: s. s nextPutAll: ' [',recipeName asString,']'! ! FactorioChest subclass: #FactorioRequesterChest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioRequesterChest methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 07:25'! initialize self extent: 1@1. super initialize. ! ! !FactorioRequesterChest methodsFor: 'as yet unclassified' stamp: 'raa 5/3/2021 07:11'! simpleName ^'logistic-chest-requester'! ! Object subclass: #FactorioRequirementsPlanner instanceVariableNames: 'luaData out raw intermediate minMax stepsInOrder needsAtStage outputsAndInputs timeList map out2 inputsToRecipes greenNodes ontoBelt allInputQtys newOrder neededOnBelt topRaw recipes topInter asmToBeltNumbers inputAveragers redNodes firstInputX beltNumToAsm debugThese interactive generateRoboports fluidInputs interBeltSwitchX lettersUsed saveTheArgs upperRow lowerRow upperBounds consumers upperBoundsSorted groupedBoundaries mainItemList allFluids middleBelts topInterPositions unitsPerSecond productionValues silent changedRequirements belt1Users belt2Users belt3Users splitOnceAlready blockedY timeScale' classVariableNames: 'SomeSilencePlease' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/17/2021 12:49'! addGreenNode: simple greenNodes add: {simple. 1}! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/27/2021 13:24'! addInputCircuitryFor: machine | ac chest insChestToAsm insFromBelt numFilters isolate cc slop toDo dx qty ddc letter needMore newGuys | newGuys _ machine entities. insFromBelt _ ac _ cc _ nil. dx _ numFilters _ 0. ddc _ -2. "dy _ -2." qty _ 50. toDo _ machine inputs reject: [ :input | machine inputStyle includesKey: input ]. toDo do: [ :input | allInputQtys add: input withOccurrences: qty. numFilters _ numFilters + 1. (toDo size > 5 and: [numFilters = 4]) ifTrue: [ insFromBelt _ ac _ cc _ nil. dx _ 1. "dy _ dy - 1." ]. insFromBelt ifNil: [ firstInputX _ firstInputX min: machine x. belt1Users add: machine x. insFromBelt _ (toDo size = 1 ifTrue: [FactorioStackFilterInserter] ifFalse: [FactorioFilterInserter]) new. insFromBelt map: map; location: dx@1; gap: 1; direction: 0; explicitControlBehavior: '"control_behavior":{"circuit_mode_of_operation":1}'. newGuys add: insFromBelt. newGuys add: (chest _ FactorioSteelChest new map: map; location: dx@2). self addGreenNode: chest. (toDo includes: 'engine-unit') ifTrue: [debugThese add: chest]. insChestToAsm _ FactorioStackInserter new map: map; location: dx@3; gap: 1; direction: 0. newGuys add: insChestToAsm. ac _ FactorioArithmeticCombinator new map: map; location: '(0)@(dy)'; direction: 2. ac explicitControlBehavior: '"control_behavior":{"arithmetic_conditions":{"first_signal":{"type":"virtual","name":"signal-each"},"second_constant":-1,"operation":"*","output_signal":{"type":"virtual","name":"signal-each"}}}'. newGuys add: ac. insFromBelt circuit: 1 color: 'red' to: ac circuit: 2. chest circuit: 1 color: 'red' to: ac circuit: 1. ]. (inputAveragers at: input) size = 1 ifTrue: [ cc ifNil: [ cc _ FactorioConstantCombinator new map: map; location: '(0)@(dy _ dy - 1)'. newGuys add: cc. cc circuit: 1 color: 'red' to: insFromBelt circuit: 1. ]. cc addFilter: input qty: 9999. ] ifFalse: [ slop _ (qty / ((inputAveragers at: input) size + 2)) floor max: 2. isolate _ FactorioArithmeticCombinator new map: map; location: '(0)@(dy _ dy - 1)'; direction: 2. isolate explicitControlBehavior: '"control_behavior":{"arithmetic_conditions":{"first_signal":{"type":"item","name":"',input,'"},"second_constant":',slop,',"operation":"+","output_signal":{"type":"item","name":"',input,'"}}}'. newGuys add: isolate. isolate circuit: 2 color: 'red' to: insFromBelt circuit: 1. redNodes add: {isolate. 1}. "input = 'engine-unit' ifTrue: [self halt]." ]. letter _ self letterFor: input. needMore _ FactorioDeciderCombinator new map: map; location: '(2)@(ddc _ ddc - 1)'; direction: 2. needMore explicitControlBehavior: '"control_behavior":{"decider_conditions":{"first_signal":{"type":"item","name":"',input,'"},"constant":',qty asString,',"comparator":"<","output_signal":{"type":"virtual","name":"signal-',letter,'"},"copy_count_from_input":false}}'. newGuys add: needMore. chest circuit: 1 color: 'red' to: needMore circuit: 1. greenNodes add: {needMore. 2}. ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/31/2021 10:54'! addUndergroundBeltFrom: arg1 to: loc2 | midX dist loc1 | loc1 _ arg1. loc2 x > loc1 x ifFalse: [self halt]. [(dist _ loc2 x - loc1 x) > 9] whileTrue: [ map addEntity: (FactorioBelt new map: map; direction: 2; location: loc1). loc1 _ loc1 + (1@0) ]. dist > 10 ifTrue: [ self halt. midX _ loc1 x + loc2 x // 2. self addUndergroundBeltFrom: loc1 to: midX@loc1 y. self addUndergroundBeltFrom: (midX+1)@loc1 y to: loc2. ^self ]. dist = 1 ifTrue: [ map addEntity: (FactorioBelt new map: map; direction: 2; location: loc1). map addEntity: (FactorioBelt new map: map; direction: 2; location: loc2). ^self ]. map addEntity: (FactorioUndergroundBelt new map: map; direction: 2; inputOrOutput: 'input'; location: loc1 ). map addEntity: (FactorioUndergroundBelt new map: map; direction: 2; inputOrOutput: 'output'; location: loc2 ). ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/31/2021 19:21'! build1Machine: machine | asm insToBelt newGuys pipe off assemblerEnd chestKind inserter firstX numInserters anyFluid outputToBelt inputStyle hasMode doBelt doBeltInput doBeltOutput outputToMiddle lastInLine firstInLine newInserter beltUsedAtY bingo | asm _ nil. outputToMiddle _ false. outputToBelt _ (consumers at: machine output ifAbsent: [#()]) size. machine widthUsed: 4. machine inputStyle: (inputStyle _ Dictionary new). "machine ioLeft ifNotNil: [ :nb | (machine inputs includes: nb output) ifTrue: [inputStyle at: nb output put: #direct]]. machine ioRight ifNotNil: [ :nb | (machine inputs includes: nb output) ifTrue: [inputStyle at: nb output put: #direct]]." machine inputs do: [ : input | (inputStyle includesKey: input) ifFalse: [ (luaData isFluid: input) ifTrue: [inputStyle at: input put: #fluid]. (topRaw includes: input) ifTrue: [inputStyle at: input put: #topRaw]. (topInter includes: input) ifTrue: [inputStyle at: input put: #topInter]. ]. (inputStyle includesKey: input) ifFalse: [ ]. ]. hasMode _ [ :xxx | inputStyle anySatisfy: [ :e | e = xxx]]. machine entities: (newGuys _ OrderedCollection new). asm _ (recipes at: machine output) newFactory map: map; direction: 4; location: 0@4. machine goal > 0 ifTrue: [asm addItems: {'speed-module-3'. 4}]. newGuys add: asm. machine primaryEntity: asm. beltUsedAtY _ Array new: 3. middleBelts withIndexDo: [ :mb :mbx | firstInLine _ lastInLine _ doBelt _ doBeltOutput _ doBeltInput _ false. mb sections do: [ :e | (machine sequence >= e firstPosition and: [machine sequence <= e lastPosition]) ifTrue: [ doBelt _ true. lastInLine _ machine sequence = e lastPosition. firstInLine _ machine sequence = e firstPosition. e item = machine output ifTrue: [outputToMiddle _ doBeltOutput _ true]. (machine inputs includes: e item) ifTrue: [doBeltInput _ true. inputStyle at: e item put: #middle]. ]. ]. beltUsedAtY at: mbx put: doBelt. doBelt ifTrue: [ firstInLine & doBeltInput ifTrue: [self halt]. doBeltOutput & doBeltInput ifTrue: [self halt]. doBeltOutput & lastInLine ifTrue: [self halt]. newInserter _ [map newInserter: (machine outputTimeFor: #ignored)]. doBeltOutput ifTrue: [ newGuys add: (newInserter value location: (asm x + 3)@(asm y + mbx - 1); gap: 1; direction: 6). ]. doBeltInput ifTrue: [ newGuys add: (newInserter value location: (asm x + 3)@(asm y + mbx - 1); gap: 1; direction: 2). ]. machine widthUsed: 6. firstInLine ifTrue: [ newGuys add: ( FactorioBelt new map: map; direction: 2; location: (asm x + 4)@(asm y + mbx - 1) ). ] ifFalse: [ newGuys add: ( FactorioUndergroundBelt new map: map; direction: 2; inputOrOutput: 'output'; location: (asm x + 4)@(asm y + mbx - 1) ). ]. lastInLine ifFalse: [ newGuys add: ( FactorioUndergroundBelt new map: map; direction: 2; inputOrOutput: 'input'; location: (asm x + 5)@(asm y + mbx - 1) ). ]. ]. '"direction":2,"type":"input"'. "into ground" '"direction":2,"type":"output"' "out of ground" ]. "machine ioLeft ifNotNil: [ :nb | (nb inputs includes: machine output) ifTrue: [ newGuys add: (FactorioStackInserter new map: map; location: (-1)@4; gap: 1; direction: 2). outputToBelt _ outputToBelt - 1. ]]. machine ioRight ifNotNil: [ :nb | (nb inputs includes: machine output) ifTrue: [ newGuys add: (FactorioStackInserter new map: map; location: (3)@4; gap: 1; direction: 6). outputToBelt _ outputToBelt - 1. ]]." outputToMiddle ifFalse: [ (topInter includes: machine output) ifTrue: [ outputToBelt > 0 ifTrue: [ newGuys add: (insToBelt _ FactorioInserter new map: map; location: (2)@7; gap: 2; direction: 0). self topInterUsed: machine. ]. ] ifFalse: [ (neededOnBelt includes: machine output) ifTrue: [ outputToBelt > 0 ifTrue: [ belt1Users add: machine x + 2. newGuys add: (insToBelt _ FactorioStackInserter new map: map; location: (2)@1; gap: 1; direction: 4). self addGreenNode: insToBelt. ontoBelt add: {insToBelt. machine output}. chestKind _ machine isFinalOutput ifTrue: [FactorioPassiveProviderChest] ifFalse: [FactorioBelt]. newGuys add: (chestKind new map: map; location: (2)@2; bar: 1). newGuys add: (map newInserter location: (2)@3; gap: 1; direction: 4). ]. ] ifFalse: [ bingo _ nil. belt1Users isEmpty ifTrue: [ 1 to: 3 do: [ :index | (beltUsedAtY at: index) ifFalse: [bingo _ index] ]. ]. bingo ifNil: [ newGuys add: (FactorioPassiveProviderChest new map: map; location: (2)@2; bar: 1). newGuys add: (map newInserter location: (2)@3; gap: 1; direction: 4). ] ifNotNil: [ newGuys add: (FactorioPassiveProviderChest new map: map; location: (4)@(bingo+3); bar: 1). newGuys add: (map newInserter location: (3)@(bingo+3); gap: 1; direction: 6). machine widthUsed: 6. ]. ]. ]. ]. (hasMode value: #topRaw) ifTrue: [ (asm testAmbiguousInputs: topRaw) ifTrue: [ inserter _ FactorioFilterInserter new addFilter: 'indirectInputs' halt first qty: 1. ] ifFalse: [ inserter _ FactorioInserter new. ]. newGuys add: (inserter map: map; location: 0@7; gap: 1; direction: 4). belt2Users add: machine x+2. belt2Users add: machine x. ]. (hasMode value: #topInter) ifTrue: [ (asm testAmbiguousInputs: topInter) ifTrue: [self halt]. asm isAssembler ifTrue: [ firstX _ 2. numInserters _ 1. anyFluid _ (hasMode value: #fluid). anyFluid ifFalse: [ "also see if we really need 2" firstX _ 1. numInserters _ 2. ]. ] ifFalse: [ firstX _ 1. numInserters _ 1. ]. 1 to: numInserters do: [ :dx | newGuys add: (FactorioInserter new map: map; location: (firstX + dx - 1)@7; gap: 2; direction: 4). ]. self topInterUsed: machine. ]. self addInputCircuitryFor: machine. machine inputs do: [ :input | (luaData isFluid: input) ifTrue: [ (machine x <= interBeltSwitchX and: [interBeltSwitchX > 0]) ifTrue: ["self halt"]. off _ asm defaultInputOffset. assemblerEnd _ asm location + off. pipe _ FactorioPipeToGround new map: map; addMaterial: input; direction: 0; location: assemblerEnd. newGuys add: pipe. "pipelineEnd _ assemblerEnd +(0@(5)). pipe _ FactorioPipeToGround new map: map; addMaterial: input; direction: 4; location: pipelineEnd. newGuys add: pipe." fluidInputs add: {pipe. input} ]. ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 07:33'! buildBlueprintBook: phase2 | builder strings w whatsit | strings _ OrderedCollection new. phase2 withIndexDo: [ :p2 :index | builder _ p2 first. whatsit _ builder saveTheArgs asArray asString. whatsit _ whatsit reject: [ :ch | '#''()' includes: ch]. builder map mapName: index asString,' - ',whatsit. strings add: builder map createBlueprintStringContents. ]. "strings explore halt." "strings _ strings first: 1." out _ FactorioOutputStream new. out nextPutAll: '{"blueprint_book":{'. out quote: 'blueprints'; colon; bracket: [ strings do: [ :e | out nextPutAll: e] separatedBy: [out comma]. ]. out "; comma; key: 'item' andValue: 'blueprint-book'; comma; key: 'active_index' andNumber: 0". out nextPutAll: '}}'. "or" ',"item":"blueprint","label":"numberOne","version":281479273906176},"index":0}'. "out contents bobEdit." w _ (FactorioTests encode: out contents asByteArray) bobEdit. w setWindowColor: Color magenta. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 07:50'! buildBlueprintBookFrom: strings | w | out _ FactorioOutputStream new. out nextPutAll: '{"blueprint_book":{'. out quote: 'blueprints'; colon; bracket: [ strings do: [ :e | out nextPutAll: e] separatedBy: [out comma]. ]. out "; comma; key: 'item' andValue: 'blueprint-book'; comma; key: 'active_index' andNumber: 0". out nextPutAll: '}}'. "or" ',"item":"blueprint","label":"numberOne","version":281479273906176},"index":0}'. "out contents bobEdit." w _ (FactorioTests encode: out contents asByteArray) bobEdit. w setWindowColor: Color magenta. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 6/1/2021 15:23'! buildCombinedMap: phase2 | builder gCopy heights maxWidth myBounds nextY phase3 r right x yBefore buildRoboportRow portsAndPoles minRawInputX checkYValues | minRawInputX _ 99999. checkYValues _ OrderedCollection new. phase3 _ OrderedCollection new. phase2 do: [ :p2 | myBounds _ p2 second. (phase3 isEmpty or: [(phase3 last detectSum: [ :b1 | b1 second height]) + myBounds height > 46]) ifTrue: [ phase3 add: OrderedCollection new. ]. phase3 last add: p2. p2 first map entities do: [ : entity | entity isRawInputFor ifNotNilDo: [ : rawdata | rawdata second do: [ :e | minRawInputX _ minRawInputX min: e x. ]. ]. ]. ]. "phase3 halt." heights _ OrderedCollection new. gCopy _ phase3 "copyFrom: 3 to: 3". nextY _ 0. buildRoboportRow _ [ :maxX | right _ maxX - 75. x _ 0. nextY = 0 ifFalse: [nextY _ nextY - 3]. portsAndPoles _ OrderedCollection new. [ portsAndPoles add: (FactorioRoboport new map: map; location: x@nextY). map entities size > 25000 ifTrue: [self halt]. #(4 29) do: [ :x2 | portsAndPoles add: (FactorioBigElectricPole new map: map; location: (x+x2)@(nextY)). ]. x < right ] whileTrue: [ x _ x + 50. ]. portsAndPoles add: (FactorioRoboport new map: map; location: (x+50)@nextY). portsAndPoles add: (FactorioBigElectricPole new map: map; location: (x+54)@(nextY)). portsAndPoles add: (FactorioRadar new map: map; location: (x+56)@nextY). [portsAndPoles allSatisfy: [ :e | map isLocationFreeFor: e]] whileFalse: [ nextY _ nextY + 1. portsAndPoles do: [ :e | e location: e location + (0@1) ]. ]. portsAndPoles do: [ :e | map addEntity: (e map: map) ]. nextY _ nextY + 4. ]. blockedY _ -999. gCopy do: [ : mmap | yBefore _ nextY. maxWidth _ 0. mmap do: [ :bbb | checkYValues add: OrderedCollection new. builder _ bbb first. r _ bbb second. checkYValues last add: nextY. nextY = 0 ifTrue: [ buildRoboportRow value: r width. yBefore _ nextY. ]. heights add: r height. r width > 1000 ifTrue: [self halt]. nextY _ nextY - 3. checkYValues last add: nextY. builder map entities do: [ :e | e location: e location - r origin + (0@nextY) ]. [builder map entities allSatisfy: [ :e | map isLocationFreeFor: e]] whileFalse: [ nextY _ nextY + 1. builder map entities do: [ :e | e location: e location + (0@1) ]. ]. checkYValues last add: nextY. builder map entities do: [ :e | map addEntity: (e map: map) ]. self buildRawInputStreamsForEntities: builder map entities minX: minRawInputX. nextY _ nextY + r height. maxWidth _ maxWidth max: r width. checkYValues last add: nextY. checkYValues last add: nextY - yBefore. ]. nextY - yBefore > 46 ifTrue: [self halt]. buildRoboportRow value: maxWidth. nextY _ nextY + 4. ]. self buildRawInputCombinators: minRawInputX. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/31/2021 20:03'! buildFluidInputs | firstFluid pipe asm goodY nextY runs x1 x999 newGuys y1 hits targetR | runs _ OrderedCollection new. fluidInputs do: [ : fi | (runs isEmpty or: [runs last last second ~= fi second]) ifTrue: [runs add: OrderedCollection new.]. runs last add: fi ]. runs do: [ :aRun | firstFluid _ aRun first. x1 _ aRun first first x. y1 _ aRun first first y. x999 _ aRun last first x. goodY _ {y1+1. y1+ 2. y1+3}. asm _ FactorioAssembler new map: map; direction: 4; recipe: (FactorioLUA LuaData recipesByName @ ('empty-',firstFluid second,'-barrel')). nextY _ firstFluid first y+1. newGuys _ OrderedCollection new. aRun do: [ :e | pipe _ FactorioPipeToGround new map: map; addMaterial: e second; direction: 4; location: e first x@goodY first. newGuys add: pipe. ]. aRun size = 1 ifTrue: [ "just 1" asm location: x1 - 1@goodY second. targetR _ firstFluid first location - (1@0) extent: 5@3. hits _ map entities select: [ :e | e intersects: targetR]. hits isEmpty ifTrue: [self halt]. hits size = 1 ifTrue: [ hits first = firstFluid first ifTrue: [ newGuys size = 1 ifFalse: [self halt]. newGuys _ OrderedCollection new. asm location: firstFluid first location - (1@0). goodY _ 'no longer meaningful'. map removeEntity: firstFluid first. "self halt." ] ifFalse: [ self halt. ]. ] ifFalse: [ "self halt." ]. ] ifFalse: [ x1 to: x999 do: [ : x | pipe _ FactorioPipe new map: map; addMaterial: firstFluid second; direction: 2; location: x@(goodY second). newGuys add: pipe. ]. asm location: x1 @ goodY third. ]. newGuys add: asm; add: (FactorioStackInserter new map: map; location: asm location + ((3)@0); gap: 1; direction: 2); add: (FactorioRequesterChest new map: map; location: asm location+ ((4)@0); addRequestFilter: firstFluid second,'-barrel' qty: 100); add: (FactorioStackInserter new map: map; location: asm location + ((3)@1); gap: 1; direction: 6); add: (FactorioActiveProviderChest new map: map; location: asm location + ((4)@1)). [newGuys allSatisfy: [ :e | map isLocationFreeFor: e]] whileFalse: [ newGuys do: [ :e | e location: e location + (0@1). e y > 100 ifTrue: [self halt]] ]. newGuys do: [ :e | map addEntity: e] ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:05'! buildInputAveragers | divide origin delta | middleBelts do: [ :e | e sections do: [ :e2 | inputAveragers removeKey: e2 item ]. ]. inputAveragers isEmpty ifTrue: [^self]. origin _ map totalBounds origin. inputAveragers keysAndValuesDo: [ : itemKey : machineList | machineList size > 1 ifTrue: [ divide _ FactorioArithmeticCombinator new map: map; direction: 2. delta _ 0@0. [ divide location: origin + delta. map isLocationFreeFor: divide ] whileFalse: [ delta _ (delta x + 1)@ delta y. delta x > 6 ifTrue: [ delta _ (0)@ (delta y+1). ]. ]. divide explicitControlBehavior: '"control_behavior":{"arithmetic_conditions":{"first_signal":{"type":"item","name":"',itemKey,'"},"second_constant":',machineList size asString,',"operation":"/","output_signal":{"type":"item","name":"',itemKey,'"}}}'. map addEntity: divide. self addGreenNode: divide. redNodes add: {divide. 2}. ]. ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 10:47'! buildLowerMachine: machine " self new createMasterRequirements: {#('rail' 15333)}. self new createMasterRequirements: {#('nuclear-reactor' 20)}. self new createMasterRequirements: {#('medium-electric-pole' 5999) }. " | asm newGuys | machine entities: (newGuys _ OrderedCollection new). asm _ (recipes at: machine output) newFactory map: map; direction: 4; location: 0@2. machine goal > 0 ifTrue: [asm addItems: {'speed-module-3'. 4}]. newGuys add: asm. machine widthUsed: 4 + (machine useNearSideInterBelt ifTrue: [1] ifFalse: [0]). "Transcript show: 'buildLowerMachine: ',asm asString; cr." (topInter includes: machine output) ifTrue: [ machine useNearSideInterBelt ifTrue: [ newGuys add: (FactorioBelt new map: map; location: (4)@(4); direction: 4). newGuys add: (FactorioBelt new map: map; location: (4)@(5); direction: 4). newGuys add: (FactorioStackInserter new map: map; location: (3)@4; gap: 1; direction: 6). ] ifFalse: [ newGuys add: (FactorioStackInserter new map: map; location: (2)@5; gap: 1; direction: 0). ]. self topInterUsed: machine. ] ifFalse: [ machine useNearSideInterBelt ifFalse: ["self halt"]. newGuys add: (FactorioPassiveProviderChest new map: map; location: (4)@(4); bar: 2). newGuys add: (FactorioStackInserter new map: map; location: (3)@4; gap: 1; direction: 6). machine widthUsed: 5. ]. (machine inputs anySatisfy: [ :input | topRaw includes: input ]) ifTrue: [ (asm testAmbiguousInputs: topRaw) ifTrue: [ "self halt." newGuys add: (FactorioFilterInserter new map: map; location: 0@1; gap: 1; direction: 0; addFilter: machine inputs first qty: 1). ] ifFalse: [ newGuys add: (FactorioInserter new map: map; location: 0@1; gap: 1; direction: 0). ]. belt2Users add: machine x+2. belt2Users add: machine x. ]. (machine inputs anySatisfy: [ :input | topInter includes: input ]) ifTrue: [ (asm testAmbiguousInputs: topInter) ifTrue: [self halt]. newGuys add: (FactorioInserter new map: map; location: 1@5; gap: 1; direction: 4). self topInterUsed: machine. ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/27/2021 13:42'! buildMap " self new createMasterRequirements: { {'advanced-circuit'. 1}}. self new createMasterRequirements: { {'satellite'. 1}}. self new createMasterRequirements: { {'logistic-chest-buffer'. 1}. {'logistic-chest-requester'. 1}. {'logistic-chest-storage'. 1}}. " | asm markBelt upperX lowerX w prev | firstInputX _ 9999. inputAveragers _ Dictionary new. ontoBelt _ OrderedCollection new. allInputQtys _ Bag new. map _ FactorioMap new. belt1Users _ OrderedCollection new. belt2Users _ OrderedCollection new. belt3Users _ OrderedCollection new. self buildNewOrder3. asm _ nil. self computeNeededOnBelt: false. beltNumToAsm _ Dictionary new. asmToBeltNumbers _ Dictionary new. newOrder withIndexDo: [ : machine :index | markBelt _ [ :bnum | (asmToBeltNumbers at: machine ifAbsentPut: [Set new]) add: bnum. ]. (topInter includes: machine output) ifTrue: [ markBelt value: 3. ] ifFalse: [ (neededOnBelt includes: machine output) ifTrue: [ markBelt value: 1. ] ]. machine inputs do: [ :input | ((luaData isFluid: input) not and: [neededOnBelt includes: input] and: [(topRaw includes: input) not]) ifTrue: [ markBelt value: 1. (inputAveragers at: input ifAbsentPut: [OrderedCollection new]) add: machine ]. ]. (machine inputs anySatisfy: [ :input | topRaw includes: input ]) ifTrue: [ markBelt value: 2. ]. (machine inputs anySatisfy: [ :input | topInter includes: input ]) ifTrue: [ markBelt value: 3. ]. ]. asmToBeltNumbers keysAndValuesDo: [ :k :v | k beltsNeeded: v asArray sorted. (beltNumToAsm at: v asArray sorted ifAbsentPut: [OrderedCollection new]) add: k ]. self splitUpperAndLowerRows. self exploreItemBoundaries. lowerX _ 0. "belt at y=0" lowerRow withIndexDo: [ : machine :index | machine location: lowerX @ 8. self buildLowerMachine: machine. machine entities do: [ :e | e location: e location + machine location. map addEntity: e. "Transcript show: '----',e asString; cr." ]. lowerX _ lowerX + machine widthUsed. ]. interBeltSwitchX _ lowerX. upperX _ 0. "belt at y=0" prev _ nil. upperRow withIndexDo: [ : machine :index | machine sequence: index; ioLeft: prev. prev ifNotNil: [prev ioRight: machine]. prev _ machine. ]. upperRow withIndexDo: [ : machine :index | (machine beltsNeeded includes: 3) ifTrue: [upperX _ upperX max: interBeltSwitchX]. machine location: upperX @ 0. self build1Machine: machine. machine addToMap: map. upperX _ upperX + machine widthUsed. ]. "middleBelts explore halt." self buildRawInput. self setOntoBeltFilters. self buildSurroundingBelt: {upperX}. self buildInputAveragers. self buildFluidInputs. map addDeferred. map addPowerPoles. map entities withIndexDo: [ :e :k | e entityID: k]. map entities do: [ :e | e removeEmptyConnections]. self connectGreenNodesByPower. self connectRedNodesByPower. self finishGreenRedConnecting. w _ map createBlueprint. interactive ifFalse: [w delete]. "self halt." ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 17:07'! buildNewOrder3 | remaining goal inputAvailable nextPass details g2 outOnly groups soleOutput usrs end start swapped tBucket isAnArg error trueGoal better choice updatedArg | changedRequirements _ OrderedCollection new. swapped _ false. details _ OrderedCollection new. "outputToInput _ Dictionary new. stepsInOrder do: [ :tuple | outputToInput at: tuple output put: tuple inputs. ]." stepsInOrder _ OrderedCollection new. groups _ OrderedCollection new. [ outOnly _ outputsAndInputs reject: [ :e | outputsAndInputs anySatisfy: [ :g | g inputs includes: e output]. ]. outOnly isEmpty ] whileFalse: [ outOnly do: [ :e | outputsAndInputs remove: e. tBucket _ timeList detect: [ :t | t first = e output] ifNone: [nil]. updatedArg _ isAnArg _ saveTheArgs detect: [ :sa | sa first = e output] ifNone: [nil]. trueGoal _ tBucket third / self timeScale. g2 _ trueGoal ceiling. goal _ g2 min: self singleRecipeLimit max: 1. isAnArg ifNotNil: [ error _ g2 / trueGoal. (1.0 - error) abs > 0.01 ifTrue: [ better _ isAnArg second * error. SomeSilencePlease ifFalse: [ choice _ self chooseFrom: #(accept skip silence quit) title: 'change ',e output,'[',g2 asString,'] from ',isAnArg second asString,' to ',better rounded asString. choice = #quit ifTrue: [self halt]. choice = #accept ifTrue: [updatedArg _ {e output. better rounded. 'was ',isAnArg asString} asArray]. choice = #silence ifTrue: [SomeSilencePlease _ true]. ]. ]. changedRequirements add: updatedArg. ]. e trueGoal: trueGoal. e goal: goal. details add: {goal. g2. tBucket. e}. ]. groups add: outOnly. ]. groups do: [ : grp | (grp sortBy: [ :a :b | a goal < b goal]) do: [ :e | e goal timesRepeat: [stepsInOrder addFirst: e copy]. ]. ]. outputsAndInputs do: [ :e | e goal timesRepeat: [stepsInOrder addFirst: e copy]. ]. 'machines replicated at this point' "halt". outputsAndInputs _ nil. inputAvailable _ Set new. inputAvailable addAll: (raw associations collect: [ :e | e key]). newOrder _ OrderedCollection new. remaining _ OrderedCollection new. soleOutput _ OrderedCollection new. consumers _ Dictionary new. stepsInOrder do: [: e | e inputs do: [ : inp | (consumers at: inp ifAbsentPut: [OrderedCollection new]) add: e ]. remaining add: e. e goal = 1 ifTrue: [soleOutput add: e] ]. "Transcript show: ((details detectSum: [ :e | e first]) asString, ' machines for ', (saveTheArgs asArray asString copyWithout: $#)); cr." details add: {details detectSum: [ :e | e first]. details detectSum: [ :e | e second]. 'total units'}. "details explore halt." [remaining isEmpty] whileFalse: [ nextPass _ OrderedCollection new. remaining do: [ :e | (e inputs allSatisfy: [ :ss | (luaData isFluid: ss) or: [inputAvailable includes: ss]]) ifTrue: [ newOrder add: e. inputAvailable add: e output. ] ifFalse: [ nextPass add: e. ] ]. remaining size = nextPass size ifTrue: [self halt]. remaining _ nextPass. ]. soleOutput do: [ :so | usrs _ consumers at: so output ifAbsent: [#()]. (usrs size = 1 and: [usrs first goal = 1]) ifTrue: [ start _ newOrder indexOf: so. end _ (newOrder indexOf: usrs first) - 1. end > start ifTrue: [ swapped ifFalse: [ start to: end-1 do: [ :i | newOrder at: i put: (newOrder at: i + 1). ]. newOrder at: end put: so. swapped _ true. ]. "self halt" ]. ]. ]. {groups. soleOutput} "explore halt". newOrder _ self shuffleCableAndCircuits: newOrder. ^newOrder ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/31/2021 07:27'! buildRawInput | n source topX topY loc1 loc2 remaining doBelt doBeltInput doBeltOutput firstInLine lastInLine asmLoc insToBelt sourceToBelt | remaining _ raw keys reject: [ : rawItem | (luaData isFluid: rawItem) or: [topRaw includes: rawItem]]. remaining _ remaining asSet. middleBelts withIndexDo: [ :mb :mbx | firstInLine _ lastInLine _ doBelt _ doBeltOutput _ doBeltInput _ false. mb sections do: [ :e | e firstPosition = 0 ifTrue: [ asmLoc _ upperRow first entities first location. remaining remove: e item. map addEntity: ( FactorioUndergroundBelt new map: map; direction: 2; inputOrOutput: 'input'; location: asmLoc + ( -1@(mbx - 1)) ). map addEntity: ( sourceToBelt _ map newInserter location: asmLoc + ( -2@(mbx - 1)); gap: 1; direction: 6 ). n _ ((raw at: e item) * 120 / self timeScale) rounded max: 100. source _ FactorioRequesterChest new map: map; addRequestFilter: e item qty: n * 5; location: asmLoc + ( -3@(mbx - 1)). map addEntity: source. source isRawInputFor: {{e item}. {source. sourceToBelt}}. ]. ]. '"direction":2,"type":"input"'. "into ground" '"direction":2,"type":"output"' "out of ground" ]. belt1Users isEmpty ifTrue: [^self]. 'got jammed against return belt' "halt". topX _ belt1Users min. topY _ 1. "below the belt now" topX >= firstInputX ifTrue: [ topX _ firstInputX - 2 ]. remaining do: [ : rawItem | insToBelt _ map newInserter. [ loc1 _ topX@topY. loc2 _ topX@(topY+1). (map isLocationFree: loc1) and: [map isLocationFree: loc2] ] whileFalse: [ topX _ topX - 1. ]. insToBelt location: loc1; gap: 1; direction: 4. map addEntity: insToBelt. self addGreenNode: insToBelt. ontoBelt add: {insToBelt. rawItem}. n _ allInputQtys occurrencesOf: rawItem. source _ FactorioRequesterChest new map: map; addRequestFilter: rawItem qty: n * 5; location: loc2. map addEntity: source. topX _ topX - 1. belt1Users add: topX. ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/31/2021 17:06'! buildRawInputCombinators: minX | xInUnderground xOutUnderground xRightOfSupply supplyBeltOffsets top temp offset | offset _ 1. supplyBeltOffsets _ #( ('copper-plate' 8) ('iron-plate' 8) ('steel-plate' 4) ('stone' 2) ('plastic-bar' 2) ('wood' 2) ) collect: [ :e | temp _ {e first. offset. e second}. offset _ offset + e second + 3. temp ]. xOutUnderground _ minX - 4. xInUnderground _ xOutUnderground - 9. xRightOfSupply _ xInUnderground - 5. supplyBeltOffsets _ supplyBeltOffsets collect: [ :e | {e first. xRightOfSupply - e second. e third} ]. top _ map totalBounds top. supplyBeltOffsets do: [ :e | map addEntity: (FactorioConstantCombinator new map: map; location: e second @ top; addFilter: e first qty: 1). ]. 2 to: 3 do: [ :i | map addEntity: (FactorioConstantCombinator new map: map; location: (xOutUnderground - i) @ top; addFilter: 'rail' qty: 1). ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/31/2021 17:06'! buildRawInputStreams | minX xInUnderground xOutUnderground xRightOfSupply supplyBeltOffsets top temp offset | self halt. offset _ 1. supplyBeltOffsets _ #( ('copper-plate' 8) ('iron-plate' 8) ('steel-plate' 4) ('stone' 2) ('plastic-bar' 2) ('wood' 2) ) collect: [ :e | temp _ {e first. offset. e second}. offset _ offset + e second + 3. temp ]. xOutUnderground _ minX - 4. xInUnderground _ xOutUnderground - 9. xRightOfSupply _ xInUnderground - 5. supplyBeltOffsets _ supplyBeltOffsets collect: [ :e | {e first. xRightOfSupply - e second. e third} ]. top _ map totalBounds top. supplyBeltOffsets do: [ :e | map addEntity: (FactorioConstantCombinator new map: map; location: e second @ top; addFilter: e first qty: 1). ]. 2 to: 3 do: [ :i | map addEntity: (FactorioConstantCombinator new map: map; location: (xOutUnderground - i) @ top; addFilter: 'rail' qty: 1). ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/31/2021 19:25'! buildRawInputStreamsForEntities: entities minX: minX | inputs y xInUnderground xOutUnderground xRightOfSupply supplyBeltOffsets materials ugb bring started currX destX save1 save2 temp offset deliveryLoc | offset _ 1. supplyBeltOffsets _ #( ('copper-plate' 8) ('iron-plate' 8) ('steel-plate' 4) ('stone' 2) ('plastic-bar' 2) ('wood' 2) ) collect: [ :e | temp _ {e first. offset. e second}. offset _ offset + e second + 3. temp ]. inputs _ entities select: [ :e | e isRawInputFor notNil]. inputs _ inputs select: [ :e | e isRawInputFor first allSatisfy: [ :rr | supplyBeltOffsets anySatisfy: [ :sbo | sbo first = rr] ] ]. inputs _ inputs sortBy: [ :a :b | a isRawInputFor second first y < b isRawInputFor second first y]. "minX _ nil. inputs do: [ : entity | entity isRawInputFor second do: [ :e | minX ifNil: [minX _ e x]. minX _ minX min: e x. ]. ]." xOutUnderground _ minX - 4. xInUnderground _ xOutUnderground - 9. xRightOfSupply _ xInUnderground - 5. supplyBeltOffsets _ supplyBeltOffsets collect: [ :e | {e first. xRightOfSupply - e second. e third} ]. "top _ map totalBounds top. supplyBeltOffsets do: [ :e | map addEntity: (FactorioConstantCombinator new map: map; location: e second @ top; addFilter: e first qty: 1). ]. 2 to: 3 do: [ :i | map addEntity: (FactorioConstantCombinator new map: map; location: (xOutUnderground - i) @ top; addFilter: 'rail' qty: 1). ]." "map entities copy do: [ :e | e x > 99999 ifTrue: [map removeEntity: e] ]." ugb _ [ :loc1 :loc2 | self addUndergroundBeltFrom: loc1 to: loc2 ]. inputs do: [ : entity | deliveryLoc _ nil. materials _ entity isRawInputFor first. entity isRawInputFor second do: [ :e | (deliveryLoc isNil or: [e x > deliveryLoc x]) ifTrue: [ deliveryLoc _ e location ]. map removeEntity: e. ]. currX _ nil. bring _ [ :bringMat : bringY | started _ false. supplyBeltOffsets reverseDo: [ : e | started ifTrue: [ destX _ e second - e third - 1. destX = currX ifTrue: [ "Transcript show: { 'equal '. destX. bringMat. e. entity} asString; cr." ]. destX > currX ifTrue: [ ugb value: currX @ bringY value: destX @ bringY. currX _ destX+1. ]. destX _ destX + e third + 2. ugb value: currX @ bringY value: destX @ bringY. currX _ destX + 1. ]. (e first = bringMat) ifTrue: [ started _ true. currX _ e second. map addEntity: (FactorioSplitter new map: map; location: currX@(bringY-1); direction: 4 ). map addEntity: (FactorioBelt new map: map; location: (currX+1)@(bringY); direction: 2 ). currX _ currX + 2. ]. ]. started ifFalse: [self halt]. ]. materials size = 1 ifTrue: [ y _ deliveryLoc y max: blockedY+2. blockedY _ y. bring value: materials first value: y. ugb value: currX@y value: (xInUnderground-1)@y. ] ifFalse: [ y _ deliveryLoc y max: blockedY+3. blockedY _ y+1. (supplyBeltOffsets findFirst: [ :e | e first = materials first]) > (supplyBeltOffsets findFirst: [ :e | e first = materials second])ifTrue: [ materials _ materials reversed ]. bring value: materials first value: y-1. save1 _ currX. bring value: materials second value: y+1. save2 _ currX. save1 = save2 ifFalse: [self halt]. map addEntity: (FactorioBelt new map: map; location: (currX-1)@(y); direction: 2 ). map addEntity: (FactorioBelt new map: map; location: (currX)@(y); direction: 2 ). map addEntity: (FactorioBelt new map: map; location: (currX)@(y-1); direction: 4 ). map addEntity: (FactorioBelt new map: map; location: (currX)@(y+1); direction: 0 ). ugb value: (currX+1)@y value: (xInUnderground-1)@y. ]. ugb value: xInUnderground@y value: xOutUnderground@y. y = deliveryLoc y ifTrue: [ ugb value: (xOutUnderground + 1)@y value: deliveryLoc. ] ifFalse: [ y < deliveryLoc y ifTrue: [self halt]. deliveryLoc y + 1 to: y do: [ :yup | map addEntity: (FactorioBelt new map: map; location: (xOutUnderground + 1)@(yup); direction: 0 ). ]. xOutUnderground + 1 to: deliveryLoc x do: [ :xover | map addEntity: (FactorioBelt new map: map; location: (xover)@(deliveryLoc y); direction: 2 ). ]. ]. ]. "map entities copy do: [ :e | (e y > 100 or: [e x > 15]) ifTrue: [map removeEntity: e] ]." ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 10:38'! buildSurroundingBelt: args | x dy cc lists begin2 end1 | x _ args first. dy _ 1. belt1Users isEmpty ifFalse: [ belt1Users min-1 to: belt1Users max+1 do: [ :x2 | "main eastward" map addEntity: (FactorioBelt new map: map; location: x2@0; direction: 2). ]. 0 to: dy-1 do: [ :y2 | "up the east side" map addEntity: (FactorioBelt new map: map; location: (belt1Users max+2)@(0-y2); direction: 0). ]. ]. firstInputX = 9999 ifFalse: [ firstInputX to: belt1Users max+3 do: [ :x2 | "upper level westward -- 1 longer to transfer to lower side" map addEntity: (FactorioBelt new map: map; location: x2@(0-dy); direction: 6). ]. 1 to: dy do: [ :y2 | "down west side" map addEntity: (FactorioBelt new map: map; location: (firstInputX-1)@(0-y2); direction: 4). ]. ]. topRaw isEmpty ifFalse: [ cc _ FactorioConstantCombinator new map: map; location: (belt2Users min-1)@(8). topRaw do: [ :rr | cc addFilter: rr qty: 1. ]. map addEntity: cc. cc isRawInputFor: {topRaw asArray. {cc}}. ]. belt2Users isEmpty ifFalse: [ belt2Users min to: belt2Users max do: [ :x2 | "lower raw" map addEntity: (FactorioBelt new map: map; location: x2@8; direction: 2). "raw right to left now" ]. ]. topInter isEmpty ifFalse: [ end1 _ begin2 _ Float infinity. topInterPositions size = 2 ifTrue: [ lists _ topInterPositions values asArray sortBy: [ :a :b | a first < b first]. end1 _ lists first last. begin2 _ lists second first. ]. belt3Users min to: belt3Users max do: [ :x2 | "lower intermediate" (x2 > end1 and: [x2 < begin2]) ifFalse: [ x2 >= interBeltSwitchX ifTrue: [ map addEntity: (FactorioBelt new map: map; location: x2@9; direction: 2). ] ifFalse: [ map addEntity: (FactorioBelt new map: map; location: x2@14; direction: 2). ]. ]. ]. (interBeltSwitchX > 0 and: [belt3Users max >= interBeltSwitchX]) ifTrue: [ 10 to: 14 do: [ :by | map addEntity: (FactorioBelt new map: map; location: interBeltSwitchX@by; direction: 0). ]. ]. ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/24/2021 11:44'! chooseFrom: labelList title: aString ^UIManager default chooseFrom: labelList values: labelList title: aString! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/16/2021 06:55'! computeNeededOnBelt: adjacencyWorks | adjacent reallyNeeded | "if every producer of x is adjacent to a consumer of x and every consumer of x is adjacent to a producer of x then no need for x on belt" neededOnBelt _ Set new. newOrder withIndexDo: [ : newTuple :index | adjacent _ OrderedCollection new. adjacencyWorks ifTrue: [ index > 1 ifTrue: [ adjacent add: (newOrder at: index - 1) ]. index < newOrder size ifTrue: [ adjacent add: (newOrder at: index + 1) ]. ]. (adjacent anySatisfy: [ :adj | adj inputs includes: newTuple output]) ifFalse: [ reallyNeeded _ newOrder anySatisfy: [ :e | e inputs includes: newTuple output]. reallyNeeded ifTrue: [neededOnBelt add: newTuple output]. ]. newTuple inputs do: [ :ox | (adjacent anySatisfy: [ :adj | adj output = ox]) ifFalse: [neededOnBelt add: ox]. ]. ]. topInter do: [ :e | neededOnBelt remove: e ifAbsent: [] ]! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/17/2021 12:53'! connectGreenNodesByPower | pole circuit gn | greenNodes do: [ : tuple | gn _ tuple first. circuit _ tuple second. pole _ map entities detect: [ :op | op isPowerPole and: [(op location dist: gn location) <= 9] ] ifNone: [nil]. gn circuit: circuit color: 'green' to: pole circuit: 1. "Transcript show: {n1. n1 location. n2. n2 location} asString; cr." ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/11/2021 14:58'! connectOneNewPoleFrom: userPoles to: linked | p2 nearby closestPoles freePoles middle d1 d2 freeWithDistance | self halt. linked do: [ : p1 | p2 _ userPoles detectMin: [ : e | e dist: p1]. p2 ifNotNil: [ (p2 dist: p1) <= 9 ifTrue: [ p1 circuit: 1 color: 'green' to: p2 circuit: 1. p1 circuit: 1 color: 'red' to: p2 circuit: 1. linked add: p2. userPoles remove: p2. ^true ]. ] ]. nearby _ OrderedCollection new. linked do: [ : p1 | p2 _ userPoles detectMin: [ : e | e dist: p1]. nearby add: {p1. p2. p2 dist: p1}. ]. closestPoles _ nearby detectMin: [ :e | e third]. freePoles _ map entities select: [ :op | op isPowerPole and: [op connections isEmpty]]. freeWithDistance _ freePoles collect: [ :e | d1 _ e dist: closestPoles first. d2 _ e dist: closestPoles second. {e. d1+d2. d1. d2} ]. middle _ freePoles detectMin: [ :e | (e dist: closestPoles first) + (e dist: closestPoles second)]. (middle dist: closestPoles second) <= 9 ifTrue: [ middle circuit: 1 color: 'green' to: closestPoles first circuit: 1. middle circuit: 1 color: 'red' to: closestPoles first circuit: 1. ^true ]. self halt. ^false! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/9/2021 16:19'! connectRedNodesByPower | pole gc gn | redNodes do: [ : tuple | gn _ tuple first. gc _ tuple second. pole _ map entities detect: [ :op | op isPowerPole and: [(op location dist: gn location) <= 9] ] ifNone: [nil]. gn circuit: gc color: 'red' to: pole circuit: 1. ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:58'! createMasterRequirements: args " self new createMasterRequirements: {#('rail' 15333)}. self new createMasterRequirements: { {'satellite'. 1}}. self new createMasterRequirements: { {'logistic-train-stop'. 1}}. self new createMasterRequirements: { {'gate'. 1}}. self new createMasterRequirements: { {'spidertron'. 1}}. self new createMasterRequirements: #bigBaseEntities. self new createMasterRequirements: { {'logistic-chest-buffer'. 1}. {'logistic-chest-requester'. 1}. {'logistic-chest-storage'. 1}}. self new createMasterRequirements: #(#('logistic-train-stop' 31) #('train-stop' 32) #('small-lamp' 157) #('constant-combinator' 92)) " | mine rdata someStuff getTop2 top2 | saveTheArgs _ args. debugThese _ Set new. luaData _ FactorioLUA LuaData. recipes _ FactorioLUA preferredRecipes. self createModRecipes. out _ String new writeStream. mainItemList _ Dictionary new. raw _ Dictionary new. intermediate _ Dictionary new. (args isSymbol ifTrue: [self perform: args] ifFalse: [args]) do: [ :sp | (self whatToBuildFor: sp first) ifNotNilDo: [ :item | mainItemList at: item put: sp second]. ]. "(self groupRequirements: mainItemList) explore halt." self explodeRequirements: mainItemList. self prepareTimeRequired. self examineSimilarInputs. "self prepareGraphic." outputsAndInputs _ OrderedCollection new. intermediate keys asArray sorted do: [ :k | rdata _ recipes at: k. mine _ rdata ingredients collect: [ :tuple |":qty :name" tuple second. ]. outputsAndInputs add: ( FactorioInputsAndOutput new inputs: mine; output: k; isFinalOutput: (mainItemList includesKey: k) ). ]. "self createStepsInOrderPhase1." "self prepareMinMax." getTop2 _ [ : fromWhat | top2 _ fromWhat associations reject: [ :e | luaData isFluid: e key]. top2 _ top2 reject: [ :e | e value < 2000]. top2 _ top2 sortBy: [ :a :b | a value > b value]. top2 _ top2 collect: [ :e | e key]. top2 size > 2 ifTrue: [top2 _ top2 first: 2]. top2 ]. topInter _ #(). "getTop2 value: intermediate." topInter _ topInter reject: [ :e | mainItemList includesKey: e]. topRaw _ getTop2 value: raw. topInterPositions _ Dictionary new. topInter do: [ :e | topInterPositions at: e put: OrderedCollection new]. "Transcript cr; show: args asString; cr. Transcript show: '--i--',ti asString; cr. Transcript show: '--r--',tr asString; cr. Transcript show: '==========='; cr." self buildMap. unitsPerSecond _ OrderedCollection new. raw keysAndValuesDo: [ :item :qty | unitsPerSecond add: {(qty / self timeScale) ceiling. 'raw'. item} ]. intermediate keysAndValuesDo: [ :item :qty | unitsPerSecond add: {(qty / self timeScale) ceiling. 'intermediate'. item} ]. someStuff _ { {'needsAtStage'. needsAtStage}. {'minMax'. minMax}. {'stepsInOrder'. stepsInOrder}. {'out contents'. out contents}. {'out2'. out2}. {'raw'. raw}. {'timeList'. timeList}. {'map'. map}. {'intermediate'. intermediate}. {'inputsToRecipes'. inputsToRecipes}. {'beltNumToAsm'. beltNumToAsm}. {'debugThese'. debugThese}. {'sortedRaw'. raw associations sortBy: [ :a :b | a value > b value]}. {'sortedintermediate'. intermediate associations sortBy: [ :a :b | a value > b value]}. {'unitsPerSecond'. unitsPerSecond} }. interactive ifTrue: [someStuff explore]. self fancySpreadsheet. productionValues _ { 'time'. self timeScale. saveTheArgs. newOrder. unitsPerSecond. }. "self halt." ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 15:57'! createModRecipes | asmTime realThing recipeName resultQty | #( ('logistic-train-stop' 1 24 ('train-stop' 1 'small-lamp' 1 'red-wire' 2 'green-wire' 2 'constant-combinator' 1)) ) do: [ :new | recipeName _ new first. resultQty _ new second. asmTime _ new third. realThing _ FactorioRecipe new. realThing recipeName: recipeName; craftingTime: asmTime; results: {{resultQty. recipeName}}; category: ''; ingredients: OrderedCollection new. new fourth pairsDo: [ : nn : qq | realThing ingredients add: { qq. nn}. ]. FactorioLUA LuaData updateRecipe: realThing. ] " FactorioLUA LuaData recipesByResult keys asArray sorted #('accumulator' 'advanced-circuit' 'arithmetic-combinator' 'artillery-shell' 'artillery-targeting-remote' 'artillery-turret' 'artillery-wagon' 'assembling-machine-1' 'assembling-machine-2' 'assembling-machine-3' 'atomic-bomb' 'automation-science-pack' 'battery' 'battery-equipment' 'battery-mk2-equipment' 'beacon' 'belt-immunity-equipment' 'big-electric-pole' 'boiler' 'burner-inserter' 'burner-mining-drill' 'cannon-shell' 'car' 'cargo-wagon' 'centrifuge' 'chemical-plant' 'chemical-science-pack' 'cliff-explosives' 'cluster-grenade' 'combat-shotgun' 'concrete' 'constant-combinator' 'construction-robot' 'copper-cable' 'copper-plate' 'crude-oil' 'crude-oil-barrel' 'decider-combinator' 'defender-capsule' 'destroyer-capsule' 'discharge-defense-equipment' 'discharge-defense-remote' 'distractor-capsule' 'effectivity-module' 'effectivity-module-2' 'effectivity-module-3' 'electric-energy-interface' 'electric-engine-unit' 'electric-furnace' 'electric-mining-drill' 'electronic-circuit' 'empty-barrel' 'energy-shield-equipment' 'energy-shield-mk2-equipment' 'engine-unit' 'exoskeleton-equipment' 'explosive-cannon-shell' 'explosive-rocket' 'explosive-uranium-cannon-shell' 'explosives' 'express-loader' 'express-splitter' 'express-transport-belt' 'express-underground-belt' 'fast-inserter' 'fast-loader' 'fast-splitter' 'fast-transport-belt' 'fast-underground-belt' 'filter-inserter' 'firearm-magazine' 'flamethrower' 'flamethrower-ammo' 'flamethrower-turret' 'fluid-wagon' 'flying-robot-frame' 'fusion-reactor-equipment' 'gate' 'green-wire' 'grenade' 'gun-turret' 'hazard-concrete' 'heat-exchanger' 'heat-pipe' 'heavy-armor' 'heavy-oil' 'heavy-oil-barrel' 'inserter' 'iron-chest' 'iron-gear-wheel' 'iron-plate' 'iron-stick' 'lab' 'land-mine' 'landfill' 'laser-turret' 'light-armor' 'light-oil' 'light-oil-barrel' 'loader' 'locomotive' 'logistic-chest-active-provider' 'logistic-chest-buffer' 'logistic-chest-passive-provider' 'logistic-chest-requester' 'logistic-chest-storage' 'logistic-robot' 'logistic-science-pack' 'long-handed-inserter' 'low-density-structure' 'lubricant' 'lubricant-barrel' 'medium-electric-pole' 'military-science-pack' 'modular-armor' 'night-vision-equipment' 'nuclear-fuel' 'nuclear-reactor' 'offshore-pump' 'oil-refinery' 'personal-laser-defense-equipment' 'personal-roboport-equipment' 'personal-roboport-mk2-equipment' 'petroleum-gas' 'petroleum-gas-barrel' 'piercing-rounds-magazine' 'piercing-shotgun-shell' 'pipe' 'pipe-to-ground' 'pistol' 'plastic-bar' 'poison-capsule' 'power-armor' 'power-armor-mk2' 'power-switch' 'processing-unit' 'production-science-pack' 'productivity-module' 'productivity-module-2' 'productivity-module-3' 'programmable-speaker' 'pump' 'pumpjack' 'radar' 'rail' 'rail-chain-signal' 'rail-signal' 'red-wire' 'refined-concrete' 'refined-hazard-concrete' 'repair-pack' 'roboport' 'rocket' 'rocket-control-unit' 'rocket-fuel' 'rocket-launcher' 'rocket-part' 'rocket-silo' 'satellite' 'shotgun' 'shotgun-shell' 'slowdown-capsule' 'small-electric-pole' 'small-lamp' 'solar-panel' 'solar-panel-equipment' 'solid-fuel' 'speed-module' 'speed-module-2' 'speed-module-3' 'spidertron' 'spidertron-remote' 'splitter' 'stack-filter-inserter' 'stack-inserter' 'steam-engine' 'steam-turbine' 'steel-chest' 'steel-furnace' 'steel-plate' 'stone-brick' 'stone-furnace' 'stone-wall' 'storage-tank' 'submachine-gun' 'substation' 'sulfur' 'sulfuric-acid' 'sulfuric-acid-barrel' 'tank' 'train-stop' 'transport-belt' 'underground-belt' 'uranium-235' 'uranium-238' 'uranium-cannon-shell' 'uranium-fuel-cell' 'uranium-rounds-magazine' 'utility-science-pack' 'water' 'water-barrel' 'wooden-chest') "! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:55'! examineSimilarInputs | allInputs | inputsToRecipes _ Dictionary new. recipes do: [ : rdata | allInputs _ rdata ingredients collect: [ :tuple |":qty :name" tuple second ]. allInputs _ allInputs asArray sorted. (inputsToRecipes at: allInputs ifAbsentPut: [allInputs -> OrderedCollection new]) value add: rdata. ]. "inputsToRecipes explore halt." ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:55'! explodeRequirements: firstList | input iter need rdata resultQty rqmt remaining | allFluids _ Set new. inputsToRecipes _ Dictionary new. iter _ 0. remaining _ firstList copy. [remaining isEmpty] whileFalse: [ iter _ iter + 1. out cr; nextPutAll: 'iteration ',iter asString; cr; cr. input _ remaining. remaining _ Dictionary new. input keysDo: [ : ik | rqmt _ input at: ik. ((self madeElsewhere includes: ik) or: [(recipes includesKey: ik) not]) ifTrue: [ (luaData isFluid: ik) ifTrue: [allFluids add: ik]. iter = 1 ifTrue: [ik inspect halt]. raw at: ik add: rqmt. out nextPutAll: ik,' (raw) ',rqmt asString; cr. ] ifFalse: [ intermediate at: ik add: rqmt. out nextPutAll: ik,' >> ',rqmt asString; cr. rdata _ recipes at: ik. resultQty _ rdata resultCountFor: ik. rdata ingredients do: [ :tuple |":qty :name" need _ (tuple first * rqmt / resultQty) rounded. out tab; nextPutAll: tuple second,' >> ',need asString; cr. remaining at: tuple second add: need. out contents size > 50000 ifTrue: [out contents bobEdit. self halt]. (inputsToRecipes at: tuple second ifAbsentPut: [Set new]) add: ik. ]. ]. ]. ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:20'! exploreItemBoundaries | list exclude | middleBelts _ OrderedCollection new. exclude _ Set new. exclude addAll: allFluids; addAll: topInter; addAll: topRaw; addAll: mainItemList keys. [ upperBounds _ Dictionary new. upperRow withIndexDo: [ : machine : index | {machine output},machine inputs do: [ : item | (exclude includes: item) ifFalse: [ list _ upperBounds at: item ifAbsentPut: [OrderedCollection new]. upperBounds at: item ifAbsentPut: [OrderedCollection new]. (list isEmpty and: [raw includesKey: item]) ifTrue: [list add: {0. nil}]. list add: {index. machine}. ]. ]. ]. upperBoundsSorted _ upperBounds associations collect: [ :e | FactorioMiddleBeltSection new item: e key; itemQty: (intermediate at: e key ifAbsent: [raw at: e key]); positions: (e value collect: [ :z | z first]); machines: (e value collect: [ :z | z second]) ]. upperBoundsSorted _ upperBoundsSorted sortBy: [ :a :b | a positions first < b positions first ]. groupedBoundaries _ OrderedCollection new. self groupBoundaries: OrderedCollection new into: groupedBoundaries. groupedBoundaries _ groupedBoundaries sortBy: [ :a :b | a totalQty > b totalQty]. groupedBoundaries isEmpty not and: [middleBelts size < 3] ] whileTrue: [ middleBelts add: groupedBoundaries first. groupedBoundaries first sections do: [ : section | exclude add: section item] ]. "groupedBoundaries explore halt. upperBounds explore." ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:56'! fancySpreadsheet " self new createMasterRequirements: { {'satellite'. 1}}. " | sheet columnFor rr headings columns final gg n | 1 = 1 ifTrue: [^self]. headings _ OrderedCollection new. columnFor _ [ :citem | headings indexOf: citem ifAbsent: [headings add: citem. headings size] ]. sheet _ String new writeStream. stepsInOrder halt withIndexDo: [ : machine : index | n _ (index + 1) asString. columns _ Dictionary new. rr _ recipes at: machine output. gg _ machine trueGoal asFloat. sheet nextPutAll: machine output; tab; "a" nextPutAll: machine goal asString; tab; "b" nextPutAll: machine trueGoal asFloat asString; tab; "c" nextPutAll: rr craftingTime asString; tab; "d" nextPutAll: '1'; tab; "e" nextPutAll: '=60*C',n,'*e',n,'/D',n,''; tab."f" sheet nextPutAll: (60/rr craftingTime * (rr resultCountFor: machine output)) asFloat asString; tab. sheet nextPutAll: (60/rr craftingTime * (rr ingredients detectSum: [ :tuple |":qty :name" tuple first])) asFloat asString; tab. columns at: (columnFor value: machine output) put: '=F',n,'*',(rr resultCountFor: machine output) asString. rr ingredients do: [ :tuple |":qty :name" columns at: (columnFor value: tuple second) put: '=F',n,'*',(tuple first) negated asString. ]. 1 to: headings size do: [ : i | sheet nextPutAll: (columns at: i ifAbsent: ['']) asString; tab ]. "rr explore halt." sheet cr. ]. final _ String new writeStream. #('recipe' 'goal' 'trueGoal' 'time' 'scale' 'permin' 'orate' 'irate') do: [ :e | final nextPutAll: e; tab. ]. headings do: [ : h | final nextPutAll: h; tab ]. final cr. final nextPutAll: sheet contents. final contents bobEdit.! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 06:56'! finishGreenRedConnecting | remove others | [ remove _ OrderedCollection new. map entities do: [ :op | (op isPowerPole and: [(others _ op connectedEntities) size = 1]) ifTrue: [ remove add: {op},others asArray ] ]. "remove explore halt." remove do: [ : tuple | tuple first removeConnectionsTo: tuple second. tuple second removeConnectionsTo: tuple first. ]. remove isEmpty ] whileFalse ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 06:51'! finishGreenRedConnectingOld1 | pole gc gn other poles linked userPoles | self halt. poles _ map entities select: [ :op | op isPowerPole]. userPoles _ poles select:[ :e | e connections notEmpty]. linked _ Set new. linked add: userPoles removeFirst. [userPoles isEmpty] whileFalse: [ (self connectOneNewPoleFrom: userPoles to: linked) ifFalse: [self halt]. ]. poles explore halt. other _ map entities select: [ :op | op isPowerPole not and: []]. 'redNodes' do: [ : tuple | gn _ tuple first. gc _ tuple second. pole _ map entities detect: [ :op | op isPowerPole and: [(op location dist: gn location) <= 9] ] ifNone: [nil]. gn circuit: gc color: 'red' to: pole circuit: 1. ]. " "! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 11:59'! groupBoundaries: soFar into: results | changed | changed _ false. upperBoundsSorted do: [ :e | (soFar isEmpty or: [soFar last positions last < e positions first]) ifTrue: [ changed _ true. self groupBoundaries: soFar,{e} into: results ] ]. changed ifFalse: [ results add: (FactorioMiddleBeltBoundaries new sections: soFar copy) ].! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 10:15'! groupRequirements: firstList " self new createMasterRequirements: #bigBaseEntities. " | input iter need rdata resultQty remaining current itemsDone oneLoop sortedStuff factoryChunks byRawInput choice targetItem targetQty | factoryChunks _ OrderedCollection new. itemsDone _ Dictionary new. firstList do: [ : request | targetItem _ request first. targetQty _ request second. itemsDone at: targetItem add: targetQty. ]. [ oneLoop _ OrderedCollection new. firstList do: [ : request | targetItem _ request first. targetQty _ request second. (itemsDone at: targetItem) > 0 ifTrue: [ iter _ 0. current _ FactorioBillOfMaterials new finishedItem: targetItem; finishedQuantity: targetQty; plannedDuration: self timeScale. oneLoop add: current. remaining _ Dictionary new. remaining at: targetItem put: targetQty. [remaining isEmpty] whileFalse: [ iter _ iter + 1. input _ remaining. remaining _ Dictionary new. input keysAndValuesDo: [ : needItem : needQty | ((self madeElsewhere includes: needItem) or: [(recipes includesKey: needItem) not]) ifTrue: [ iter = 1 ifTrue: [needItem inspect halt]. current rawItemsAndQuantities at: needItem add: needQty. ] ifFalse: [ iter = 1 ifFalse: [current intermediateItemsAndQuantities at: needItem add: needQty]. rdata _ recipes at: needItem. resultQty _ rdata resultCountFor: needItem. rdata ingredients do: [ :tuple |":qty :name" need _ (tuple first * needQty / resultQty) rounded. remaining at: tuple second add: need. ]. ]. ]. ]. ]. ]. oneLoop isEmpty ] whileFalse: [ oneLoop do: [ :e | e computeSubsetBillsFrom: oneLoop]. byRawInput _ oneLoop sortBy: [ :a :b | a totalRawInput > b totalRawInput]. sortedStuff _ { oneLoop sortBy: [ :a :b | a numberOfComponents > b numberOfComponents]. byRawInput. oneLoop sortBy: [ :a :b | a subsetBills size > b subsetBills size]. }. choice _ byRawInput first. factoryChunks add: choice. "choice ? 'express' ifTrue: [self halt]." {choice},choice subsetBills do: [ :dd | itemsDone at: dd finishedItem add: dd finishedQuantity negated ]. ]. ^factoryChunks ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/24/2021 11:48'! initialize super initialize. interactive _ true. redNodes _ OrderedCollection new. greenNodes _ OrderedCollection new. fluidInputs _ OrderedCollection new. lettersUsed _ Dictionary new. silent _ false.! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/14/2021 07:02'! interactive: x interactive _ x! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/17/2021 13:03'! letterFor: item ^lettersUsed at: item ifAbsentPut: [(lettersUsed size + $A asciiValue) asCharacter asString] ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/21/2021 06:59'! madeElsewhere ^#('steel-plate' 'copper-plate' 'iron-plate' 'stone' 'coal' 'sulfur' 'lubricant' 'sulfuric-acid' 'plastic-bar' 'water' 'crude-oil' 'heavy-oil' 'light-oil' 'rocket-fuel')! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/14/2021 06:44'! map ^map! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 13:58'! middleBelts ^middleBelts! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 16:32'! newOrder ^newOrder! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:58'! prepareGraphic | allInputs col columnNumbers graphic keySorter maxX maxY mine newKey rdata x y | graphic _ Morph new. columnNumbers _ Dictionary new. y _ 20. maxX _ maxY _ 0. allInputs _ Bag new. intermediate keys asArray sorted do: [ :k | rdata _ recipes at: k. rdata ingredients do: [ :tuple |":qty :name" allInputs add: tuple second. ]. ]. allInputs _ allInputs asSet asArray sortBy: [ :a :b | (allInputs occurrencesOf: a) > (allInputs occurrencesOf: b)]. keySorter _ OrderedCollection new. intermediate keys asArray sorted do: [ :k | rdata _ recipes at: k. mine _ rdata ingredients collect: [ :tuple |":qty :name" tuple second. ]. newKey _ String new. allInputs do: [ :e | newKey _ newKey ,( (mine includes: e) ifTrue: ['1'] ifFalse: ['0'])]. keySorter add: {k. newKey}. ]. keySorter _ keySorter sortBy: [ :a :b | a second > b second]. keySorter _ keySorter collect: [ :e | e first]. keySorter do: [ :k | rdata _ recipes at: k. graphic addMorph: (Morph new color: Color gray; position: 20@y; extent: 20@20; balloonText: k). rdata ingredients do: [ :tuple |":qty :name" col _ allInputs indexOf: tuple second. x _ col * 30 + 30. maxX _ maxX max: x + 20. graphic addMorph: (Morph new color: Color brown; position: x@y; extent: 20@20; balloonText: tuple second). ]. maxY _ y + 20. y _ y + 30. ]. graphic extent: (maxX@maxY) + (40@40); color: Color paleBlue; openInWorld. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/21/2021 14:12'! prepareMinMax "not important at this time" | temp | minMax _ Dictionary new. stepsInOrder halt withIndexDo: [ :e : index | {e output},e inputs do: [ :item | (minMax at: item ifAbsentPut: [OrderedCollection new]) add: index ] ]. needsAtStage _ (1 to: stepsInOrder size) collect: [ :j | temp _ OrderedCollection new. minMax keysAndValuesDo: [ :k :v | (j between: v first and: v last) ifTrue: [temp add: k]. ]. temp ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 12:56'! prepareTimeRequired | qty rdata secs | timeList _ OrderedCollection new. intermediate keys asArray sorted do: [ :k | rdata _ recipes at: k. qty _ intermediate at: k. secs _ rdata craftingSecondsFor: k qty: qty at: #modded. timeList add: {k. qty. secs}. ]. out cr; cr; nextPutAll: 'item'; tab; nextPutAll: 'qty'; tab; nextPutAll: 'time'; cr. timeList _ timeList sortBy: [ :a :b | a third > b third]. timeList do: [ :e | e do: [ :ee | out nextPutAll: ee asString; tab]. out cr]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/17/2021 16:10'! printOn: s super printOn: s. s space; print: saveTheArgs! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 14:43'! reportBottlenecks: planners | report split threeNums middleItems ss splitters what datum | report _ String new writeStream. splitters _ OrderedCollection new. planners do: [ :p | split _ 1. middleItems _ Set new. report cr; nextPutAll: '-------------------------------'; cr; nextPutAll: '[',p newOrder size asString,'] ',p saveTheArgs asArray asString; cr; nextPutAll: '-------------------------------'; cr. p middleBelts withIndexDo: [ :mb :mbx | report nextPutAll: 'mb: ',mbx asString; cr. mb sections withIndexDo: [ :sec :secx | middleItems add: sec item. threeNums _ sec onOffBottleneck. report tab; nextPutAll: sec item asString,' ',threeNums asString; cr. split _ split max: (threeNums third / 22.5) ceiling. ]. ]. report nextPutAll: '+ups+'; cr. p unitsPerSecond do: [ :ups | (luaData isFluid: ups third) ifFalse: [ ups first > 22 ifTrue: [report nextPutAll: '>>']. report tab; nextPutAll: ups asString. (middleItems includes: ups third) ifFalse: [ split _ split max: (ss _ (ups first / 22.5) ceiling). report nextPutAll: ' SPLIT: ',ss asString ]. report cr. ]. ]. split > 1 ifTrue: [ datum _ {p saveTheArgs detectMax: [ :uu | uu second]. split}. report nextPutAll: 'TRY SPLITTING: ',datum asString; cr. splitters add: datum. ]. ]. report cr; cr. report nextPutAll: splitters asString. report contents "bobEdit". what _ #split. "self chooseFrom: #(ignore split halt) title: 'What to do about big entries'." what = #halt ifTrue: [self halt]. what = #ignore ifTrue: [^#()]. ^splitters! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 09:38'! saveTheArgs ^saveTheArgs! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/17/2021 13:33'! setOntoBeltFilters | n | true ifTrue: [ ontoBelt do: [ :ee | ee first explicitControlBehavior: '"control_behavior":{"circuit_condition":{"first_signal":{"type":"virtual","name":"signal-',(self letterFor: ee second),'"},"constant":0,"comparator":">"}}'. ]. ] ifFalse: [ ontoBelt do: [ :ee | n _ allInputQtys occurrencesOf: ee second. n _ (n * 0.9) rounded max: 20. ee first explicitControlBehavior: '"control_behavior":{"circuit_condition":{"first_signal":{"type":"item","name":"',ee second,'"},"constant":',n asString,',"comparator":"<"}}'. ]. ]. '"circuit_condition":{"first_signal":{"type":"virtual","name":"signal-A"},"constant":0,"comparator":">"}'. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/29/2021 10:54'! shuffleCableAndCircuits: newOrder | these part1 order part2 bingo result | these _ #('advanced-circuit' 'electronic-circuit' 'copper-cable'). part1 _ newOrder select: [ :e | these includes: e output]. part2 _ newOrder reject: [ :e | these includes: e output]. result _ OrderedCollection new. [part1 isEmpty] whileFalse: [ order _ (part1 anySatisfy: [ :e | e output = 'advanced-circuit']) ifTrue: [ #( 'copper-cable' 'electronic-circuit' 'copper-cable' 'advanced-circuit'). ] ifFalse: [ #( 'copper-cable' 'copper-cable' 'electronic-circuit'). ]. order do: [ :type | bingo _ part1 detect: [ :e | e output = type] ifNone: [nil]. bingo ifNotNil: [ result add: bingo. part1 remove: bingo. ]. ] ]. result _ result,part2. "result explore halt." ^result ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/19/2021 07:04'! singleRecipeLimit ^12! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 19:38'! splitUpperAndLowerRows | interLower interUpper | upperRow _ newOrder select: [ :e | e beltsNeeded includes: 1]. lowerRow _ newOrder reject: [ :e | e beltsNeeded includes: 1]. lowerRow isEmpty ifFalse: [Transcript show: 'lower ',lowerRow asString; cr.]. lowerRow size < 3 ifTrue: [ upperRow _ lowerRow,upperRow. lowerRow _ #(). ]. topInter isEmpty ifTrue: [ upperRow do: [ :e | e useNearSideInterBelt: false]. lowerRow do: [ :e | e useNearSideInterBelt: false]. ^self ]. interUpper _ Set new. interLower _ Set new. upperRow do: [ :e | (topInter includes: e output) ifTrue: [interUpper add: e output]]. lowerRow do: [ :e | (topInter includes: e output) ifTrue: [interLower add: e output]]. interUpper isEmpty ifTrue: [ upperRow do: [ :e | e useNearSideInterBelt: false]. lowerRow do: [ :e | e useNearSideInterBelt: topInter first ~= e output]. ^self. ]. interLower isEmpty ifTrue: [ interUpper size = 1 ifFalse: [self halt]. upperRow do: [ :e | e useNearSideInterBelt: topInter first ~= e output]. lowerRow do: [ :e | e useNearSideInterBelt: false]. ^self ]. interLower size = interUpper size ifFalse: [self halt]. upperRow do: [ :e | e useNearSideInterBelt: false]. lowerRow do: [ :e | e useNearSideInterBelt: true]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 6/1/2021 15:12'! timeScale ^timeScale ifNil: [timeScale _ 36000]! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 6/1/2021 15:13'! timeScale: x timeScale _ x! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/27/2021 13:27'! topInterUsed: machine belt3Users add: machine x+2. belt3Users add: machine x. {machine output},machine inputs do: [ :item | topInterPositions at: item ifPresent: [ :poslist | poslist add: machine x; add: machine x+ 2] . ]. ! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 09:39'! unitsPerSecond ^unitsPerSecond! ! !FactorioRequirementsPlanner methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 21:25'! whatToBuildFor: x x = '' ifTrue: [^nil]. x = 'straight-rail' ifTrue: [^'rail']. x = 'curved-rail' ifTrue: [^'rail']. x= 'basic-robots-roboport' ifTrue: [^nil]. x = 'basic-robots-logistic-chest-passive-provider' ifTrue: [^nil]. ^x ! ! !FactorioRequirementsPlanner methodsFor: 'parts lists' stamp: 'raa 5/12/2021 11:51'! bigBaseEntities ^#( "#('' 1) " #('accumulator' 6019) #('arithmetic-combinator' 75) #('artillery-turret' 18) #('assembling-machine-1' 4) #('assembling-machine-2' 334) #('assembling-machine-3' 1173) #('basic-robots-logistic-chest-passive-provider' 16) #('basic-robots-roboport' 29) #('beacon' 1474) #('big-electric-pole' 431) #('centrifuge' 13) #('chemical-plant' 151) #('constant-combinator' 92) #('curved-rail' 691) #('decider-combinator' 5) #('electric-furnace' 935) #('electric-mining-drill' 2068) #('express-splitter' 1064) #('express-transport-belt' 44631) #('express-underground-belt' 2240) #('fast-inserter' 5845) #('fast-splitter' 100) #('fast-transport-belt' 3179) #('fast-underground-belt' 441) #('filter-inserter' 30) #('flamethrower-turret' 1) #('gate' 4) #('gun-turret' 1) #('heat-exchanger' 192) #('heat-pipe' 398) #('inserter' 259) #('lab' 137) #('laser-turret' 2916) #('logistic-chest-active-provider' 61) #('logistic-chest-buffer' 2) #('logistic-chest-passive-provider' 225) #('logistic-chest-requester' 175) #('logistic-chest-storage' 124) #('logistic-train-stop' 31) "#('logistic-train-stop-input' 31) #('logistic-train-stop-output' 31) " #('long-handed-inserter' 590) #('medium-electric-pole' 5999) #('nuclear-reactor' 16) #('offshore-pump' 22) #('oil-refinery' 19) #('pipe' 2135) #('pipe-to-ground' 2497) #('power-switch' 1) #('programmable-speaker' 1) #('pump' 44) #('pumpjack' 62) #('radar' 260) #('rail-chain-signal' 148) #('rail-signal' 383) #('roboport' 1145) #('rocket-silo' 1) #('small-electric-pole' 4580) #('small-lamp' 157) #('solar-panel' 8501) #('splitter' 2) #('stack-filter-inserter' 571) #('stack-inserter' 1818) #('steam-turbine' 384) #('steel-chest' 995) #('steel-furnace' 102) #('stone-wall' 19109) #('storage-tank' 59) #('straight-rail' 15333) #('substation' 2351) #('train-stop' 32) #('transport-belt' 711) #('underground-belt' 311) #('wooden-chest' 201))! ! !FactorioRequirementsPlanner methodsFor: 'parts lists' stamp: 'raa 5/25/2021 06:56'! militaryScience ^{{'military-science-pack'. 2250}}! ! !FactorioRequirementsPlanner methodsFor: 'parts lists' stamp: 'raa 5/24/2021 07:00'! rcu ^#(#('rocket-control-unit' 750) )! ! !FactorioRequirementsPlanner methodsFor: 'parts lists' stamp: 'raa 5/24/2021 14:21'! rcuMany ^#(#('rocket-control-unit' 750) #('rocket-control-unit' 750) #('rocket-control-unit' 750) #('rocket-control-unit' 750) )! ! !FactorioRequirementsPlanner methodsFor: 'parts lists' stamp: 'raa 5/22/2021 20:58'! roccketPartInputs ^#(#(10 'rocket-control-unit') #(10 'low-density-structure') "#(10 'rocket-fuel')") collect: [ :e | {e second. 200}]! ! !FactorioRequirementsPlanner methodsFor: 'parts lists' stamp: 'raa 5/27/2021 10:55'! sixScience ^#('utility-science-pack' 'automation-science-pack' "'military-science-pack' " 'logistic-science-pack' 'chemical-science-pack' 'production-science-pack') collect: [ :e | {e. 2500}]! ! !FactorioRequirementsPlanner methodsFor: 'doit' stamp: 'raa 6/1/2021 15:15'! createLevelZeroRequirements: args " [self new createLevelZeroRequirements: {#bigBaseEntities. true. 36000}] timeToRun. self new createLevelZeroRequirements: {#sixScience. true. 1000}. self new createLevelZeroRequirements: {#roccketPartInputs. true}. self new createLevelZeroRequirements: {#rcu. true}. self new createLevelZeroRequirements: {#rcu. 6}. self new createLevelZeroRequirements: #(militaryScience. 1) " | phase1 miniList builder w phase2 splitTheseGuys okSplit before pass data n strings whatsit attempts | attempts _ OrderedCollection new. SomeSilencePlease _ true. Transcript cr; nextPutAll: '***********************'; cr. splitOnceAlready _ Set new. saveTheArgs _ args. debugThese _ Set new. luaData _ FactorioLUA LuaData. recipes _ FactorioLUA preferredRecipes. self createModRecipes. out _ String new writeStream. pass _ 0. splitTheseGuys _ #(). mainItemList _ OrderedCollection new. args size >= 3 ifTrue: [timeScale _ args third]. 1 to: (args second isNumber ifTrue: [args second] ifFalse: [1]) do: [ :i | mainItemList addAll: (self perform: args first)]. [ pass _ pass + 1. redNodes _ greenNodes _ fluidInputs _ lettersUsed _ 'what??'. phase2 _ OrderedCollection new. raw _ Dictionary new. intermediate _ Dictionary new. before _ mainItemList. mainItemList _ OrderedCollection new. before do: [ :sp | (self whatToBuildFor: sp first) ifNotNilDo: [ :item | okSplit _ splitTheseGuys detect: [ :guy | guy first first = sp first ] ifNone: [#(1 1)]. n _ okSplit second. data _ {item. (sp second / n) ceiling}. n > 1 ifTrue: [ Transcript show: n asString,' x ',data asString; cr. "splitTheseGuys isEmpty ifFalse: [self halt]." ]. n timesRepeat: [ mainItemList add: data ]. ]. ]. phase1 _ self groupRequirements: mainItemList. map _ FactorioMap new. (phase1 "copyFrom: 2 to: 2") do: [ : gg | miniList _ OrderedCollection new. {gg},gg subsetBills do: [ :e | miniList add: {e finishedItem . e finishedQuantity}. ]. miniList ? 'assembling-machine-1' ifTrue: ["self halt"]. builder _ self class new. builder interactive: false; timeScale: self timeScale; createMasterRequirements: miniList. phase2 add: {builder. builder map totalBounds}. ]. phase2 _ phase2 sortBy: [ :a :b | a second width > b second width]. attempts add: phase2. "phase2 _ phase2 copyFrom: 3 to: 3." splitTheseGuys _ self reportBottlenecks: (phase2 collect: [ :e | e first]). splitTheseGuys _ splitTheseGuys reject: [ :e | splitOnceAlready includes: e first first]. splitTheseGuys do: [ :e | splitOnceAlready add: e first first]. Transcript show: 'pass ',pass asString,' = ',splitTheseGuys asString; cr; cr. splitTheseGuys isEmpty ] whileFalse. strings _ OrderedCollection new. "phase2 _ phase2 copyFrom: 1 to: 1." phase2 withIndexDo: [ :p2 :index | builder _ p2 first. whatsit _ builder saveTheArgs asArray asString. whatsit _ whatsit reject: [ :ch | '#''()' includes: ch]. builder map mapName: index asString,' - ',whatsit. strings add: builder map createBlueprintStringContents. ]. self buildCombinedMap: phase2. map mapName: 'everything'. strings addFirst: map createBlueprintStringContents. self buildBlueprintBookFrom: strings. w _ map createBlueprint. w setWindowColor: Color yellow. {map totalBounds extent. self. attempts} explore. ! ! FactorioMapEntity subclass: #FactorioRoboport instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioRoboport methodsFor: 'as yet unclassified' stamp: 'raa 5/14/2021 08:22'! initialize super initialize. self extent: 4@4.! ! !FactorioRoboport methodsFor: 'as yet unclassified' stamp: 'raa 5/14/2021 09:38'! simpleName ^'roboport'! ! FactorioMaker subclass: #FactorioSmelter instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioSmelter methodsFor: 'as yet unclassified' stamp: 'raa 4/21/2021 15:25'! entityGraphicName ^{'entity/electric-furnace/electric-furnace-base.png'. 129@100}! ! !FactorioSmelter methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 09:38'! initialize self extent: 3@3. super initialize. ! ! !FactorioSmelter methodsFor: 'as yet unclassified' stamp: 'raa 5/16/2021 08:13'! isSmelter ^true! ! !FactorioSmelter methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 14:43'! overlayName ^recipe recipeName! ! !FactorioSmelter methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 13:49'! simpleName ^'electric-furnace'! ! !FactorioSmelter methodsFor: 'as yet unclassified' stamp: 'raa 5/16/2021 08:25'! testAmbiguousInputs: x | n | n _ #('iron-plate' 'stone' 'iron-ore' 'copper-ore') count: [ :e | x includes: e]. ^n > 1 ! ! FactorioInserter subclass: #FactorioStackFilterInserter instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioStackFilterInserter methodsFor: 'as yet unclassified' stamp: 'raa 5/10/2021 07:03'! simpleName ^'stack-filter-inserter'! ! FactorioInserter subclass: #FactorioStackInserter instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioStackInserter methodsFor: 'as yet unclassified' stamp: 'raa 5/4/2021 12:14'! simpleName ^'stack-inserter'! ! FactorioChest subclass: #FactorioSteelChest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioSteelChest methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 19:44'! initialize self extent: 1@1. super initialize. ! ! !FactorioSteelChest methodsFor: 'as yet unclassified' stamp: 'raa 5/20/2021 21:55'! simpleName ^'steel-chest'! ! FactorioMapEntity subclass: #FactorioSubstation instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioSubstation methodsFor: 'as yet unclassified' stamp: 'raa 4/26/2021 06:28'! entityGraphic | f | f _ Form extent: 32@32 depth: 32. f fillColor: Color orange. ^f! ! !FactorioSubstation methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 06:53'! initialize self extent: 2@2. super initialize. ! ! !FactorioSubstation methodsFor: 'as yet unclassified' stamp: 'raa 5/9/2021 16:04'! isPowerPole ^true! ! !FactorioSubstation methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:24'! showBorder ^false! ! !FactorioSubstation methodsFor: 'as yet unclassified' stamp: 'raa 5/27/2021 18:49'! simpleName ^'substation'! ! !FactorioSubstation methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 06:52'! supplyArea ^18@18! ! !FactorioSubstation methodsFor: 'as yet unclassified' stamp: 'raa 5/28/2021 06:52'! wireReach ^18! ! Object subclass: #FactorioTests instanceVariableNames: 'recipes' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioTests methodsFor: 'as yet unclassified' stamp: 'raa 4/11/2021 10:23'! buildRecipes " WebUtils jsonDecode: (StandardFileStream readOnlyFileNamed: '/Users/bob/Desktop/rawFactorio.txt') " recipes _ Dictionary new. #( (1 RedScience 1 CopperPlate 1 IronGear) (1 GreenScience 1 YellowBelt 1 YellowInserter) (1 GrayScience 1 Grenade 1 RedAmmo 2 Wall) (2 BlueScience 1 Sulfur 3 RedCircuit 2 EngineUnit) (3 PurpleScience 30 Rail 1 ElectricFurnace 1 Prod1Module) (3 YellowScience 2 BlueCircuit 1 FlyingRobotFrane 3 LowDensityStructure) (1 IronGear 2 IronPlate) (2 YellowBelt 1 IronPlate 1 IronGear) ) do: [ :e | recipes at: e second put: e ] ! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! FactorioTests class instanceVariableNames: ''! !FactorioTests class methodsFor: 'as yet unclassified' stamp: 'raa 5/12/2021 10:59'! decode: mayHaveZero " self decode: self string4 " | check cmf compressionInfo compressionMethod decode flg original yy aString strm what freqs strings | aString _ mayHaveZero. (aString beginsWith: '0') ifTrue: [aString _ aString allButFirst]. decode _ (Base64MimeConverter mimeDecodeToBytes: aString readStream) contents. "decode inspect." "f _ FileStream fileNamed: 'test1.zlib'. f binary; nextPutAll: decode. f close." cmf _ decode at: 1. flg _ decode at: 2. check _ cmf *256 + flg. compressionMethod _ cmf bitAnd: 15. compressionInfo _ (cmf bitShift: -4) bitAnd: 15. {cmf. flg. check. check \\ 31. compressionMethod. compressionInfo} explore. yy _ ZLibReadStream on: decode from: 1 to: decode size. "yy inspect." original _ yy contents asString. strings _ Bag new. strm _ original readStream. [strm atEnd] whileFalse: [ strm upToAll: '{"entity_number":'. strm upToAll: '"name":"'. what _ strm upTo: $". strings add: what. ]. freqs _ strings sortedElements asArray collect: [ :e | {e key. e value}]. freqs inspect. "f _ FileStream fileNamed: 'test1.json'. f nextPutAll: original. f close." ^original! ! !FactorioTests class methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 13:28'! encode: jsonBlueprint | aStream encoded z zipped | aStream _ WriteStream on: ByteArray new. z := ZLibWriteStream on: aStream. z nextPutAll: jsonBlueprint. z close. zipped _ aStream contents. encoded _ Base64MimeConverter mimeEncode: zipped readStream. "{aStream. z. zipped. encoded} explore." ^'0', encoded contents. ! ! !FactorioTests class methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 13:47'! myOwnString "removed 0 from fromt" ^'eF49jlELwyAMhP/LPSt07M2/MsqwLnQBjaIprBT/++wKezm45O5LDixxo1JZFO4AhywN7nGg 8So+njPdC8GBlRIMxKfT+dYoLZFltcmHNwvZO7oBy4s+cLc+G5AoK9PF+5n9KVtaqI7An0SR glYOtiWKOnYGJbdRzHJeH7DJYB866JUCX8/ULLZEr4Q+9/4FdmRDpQ=='! ! !FactorioTests class methodsFor: 'as yet unclassified' stamp: 'raa 6/27/2020 11:37'! replaceOldIn: s | new | new _ s. #( ('science-pack-1' 'automation-science-pack') ('science-pack-2' 'logistic-science-pack') ('science-pack-3' 'chemical-science-pack') ('high-tech-science-pack' 'utility-science-pack') ) do: [ : tuple | new _ new copyReplaceAll: tuple first with: tuple second ]. ^new! ! !FactorioTests class methodsFor: 'as yet unclassified' stamp: 'raa 6/27/2020 09:56'! string1 " 0 removed from front " ^'eNrkvdtuZEmWHfgrgXgQJCA9YLbtnpgXVWNUeuiSGl39MhAKBQ+GZ6RPMkgOycjsUiM+QB+iH9OXyHk/JO2y1j7mwUrNgy4dndy+zY7d97r82/uPp193F5f7s+u/fjw//+X9j//29C9X73/8b4v/8eZ/tz85P7v756v957Pt6c2/Xf/tYvf+x/f7692X9z+8P9t+ufmftldXuy8fT/dnnzdftic/7892G/v+2w/v92efdv/6/kf77YdhiOvL7dnVxfnl9ebj7vR68ccC/PH+8vxs83m3vdz89vNud7r4a/ftLz+8351d76/3u7um3P4Pf/vr2dcvH3eXh9z6jfjh/cX51eGPz89ufvwQcHP4g78d/p9DWu8vdyf7i2ryr35GHn/m6sv29HSzO92dXF/uTzYX56e717/y+COVUO6p2WdXu8vrw7+9zlJuA5hDkp/2hzTv/le+Es0D0e6zsc+jxUq0AEQzaGrxMdhv5+efdmebk593V9evA8pDepUYif287jbYs6/7enS9+pmMfJO7bnzRi1KJVh6j3UzA6+3Z9ebk/MvH/dn2+rwd+Cbnw39/fXl++tePu5+3v+4P//Hhv/hpf3rIB5rHty29ON1e797fBvt6sw7YxTz+S62PrQE/lLnvgFoMC49DYBhacrqZ+w6shXLkCNINIIvPQzceQDZQX8R9u1klb4fCj4sd4If3p9vDenb4N/vh3R8P6V+9+3fv/p/d6en5b+/+cFjnrg7/xa+HkXX3GXISybaYJE+rr7nJ7c22lKuT/e7sZLe52J788uyPkS3lbsScnx3GzMn+8uTr/vp77CqeHDZP4/zwqV6H9XdRXX8bAVMMlY2v2kuqPeZ+x7oNP9gXwry9NML7Vf3vE9KycL/hafcNV91++xtHJ5xH99/Fug519H2iZrC+91ekTW83X6zrw30WOa4sFndmpzXfYacdr1An5xcXu8tOEGls1545QPZ3F2zpeOq1x4Xj1dr8+leQuelqY7n6pZGZ6qsDpzo1MjicfacbCzolQvvcJGbCAfd+JJ1sP57uqruMRbaZ6i4j5EnMdZrq4E9ogQPNP+8+vfvz3Sg8HGv+eLnbnb37h7s9bHywsfqDzcevl2eHzv6yP7v5Up8u96en5LlmcDRCTjdX1+eHv/rpkMr2ZPfsYIP+7cfDN/xl8ZcePxLVeuD1ODUfHg4cH8J4QgoZXe6jGyQ4e1QylfXuRYd3zkhQA6j8Q+O7t7vlps9XHZcGp6VBHoM0suYAWM2osPek7ilpeCZxwEZl9RfB4Zr355tuf/ef7rr95jb3h5tZPF7uRL/cXV3vtl82u7PPh7nybJ2Dlwvs7GnNww0KeH073X65aJ6M6yGc6ixcj/U01y9uVofmGbj6x6H1ZNq6uPj+TK6n0MsgQb3Z68ysORTUQxVyLeh1yvLCsxy2ldFmq7dV6U/oQdCIx1xsd+f70/oB/kOoxhvcg8b3KiQ/zw5SGVw0wDD1FToOe6vVWaK9Sli87zM4PojhUcCQxDAWdm4AzxVCTg0kJDEzgAeCxZ0DHIH1qws7H6oXRgnwOEZ6KlLjDomYuGGHhJxbWJFCfofq13SGjFIv402teDjRnZfqT8OOOS/VQ3jVeakeK2DnpfofR+6J765b/PoXvpPz7SlTRXOJHFX15mboYFf/20Ic7KoRvNEc7OqhrOpgNyxlyc0t6LA4vfun899uZ1z/5uP0N5/HhJtPPfAVqB7q9TwocA1F6NgRjs1elRJ0VULzdIo6EhjbwKGjpv685rZlodsW2E68CwsbGh9FbPWpN4osPZXwmWRFOU1HcIPRLvCwhZup97H75AwFMuDWEBlc0oYpZjhFfE+9X4WQoBkPGuGg+Mn0sZ7M3Nm41bP6hRa3tWGKHk5R8KAODup0K7usvLvVUXfSv8qNpzkcNNK3TOZON4qJ56nckOqfB5848NchLnjwmHRWt1XW7474rIFnosN3HngBcvjcgVdKh08deElfXBKp84GsvMjBG5nD9xx4/3b4zMFPGuwN8H7b9TOugKYTSohTdD2C0xxH/eg66j68+8P2an/y7k+3w214H/VrKnH1mr9FAfaPSCMCaPASpPQ9cQbjndVDNUM4TgAmHRFV4KjEEcU6OGqgryJI1Ai96GI34nFeFs4rs/cPJChZnXyIPKpOgpc4JEMIb/AQzw9AmeB9CMoLKknGXl6evfxBeQX2FgRFjez1D4pKwJMbfZjZyw+UVyEA4X4AewSvjUheYieCPIibLb54imPvjVBUT5T//bd191l8mAiyVXRHCXt5hbLK5N0LCkpuFF2kEnt7hc4tFkZjN7IS8ioIZYVsEr09grig4ssucUPFTxIuktdeKCiyQfQe1ol7KXFEdMj+0D0iekPel6FTNrk99A5NHp8OxBnWI/PB9hZy7/lrT/UVxAc+UL0QHGH0sn2GGa/FghHImzwKleFQcRSqwKH8IFQwNNS7GcrCoUbdHgQNZUeRHBrJjSJ5NFIYRYLh9WkUCR7qZRQJHuh22OWZ5cjFqSS588vVFLlWCJgpU/1rV8fxhMIvfFWYWTR0IERUYTGtL073142z/v2YeEUvqb7oCP2oBTD2ooMSdY1EqyE98/ZU/yjhGPX5GHXvRPUU0xEq6TEfAUEQC/H2VJf3MMQrUT2CPUI1P8kRcAfJEa859bb6I1TwUzgCciHFIxTdUyKeneodmI+AVkhF9+pUzTCb+RiAbI8AVsgclb8Wwc2v/GePPznVkwrz8RI5zq/256R6cKq3Oc+HS+SCvzdVkypmPvSgWPy5qZ6UzMdYFDcfblD8fIxFCfhbU73z4hHwCiURT031tI4AzShF9dJU10wx+Eyw8EJpjWUfsKCoQjxgNZqLzwfr8cTwGWEdHjVwd2sbfl/6MzTIA71kWxN1rxL2+xN7rEn0RR6RoDGZDgvB7p/Wnq+Hdl1+vjy081MzcJ1v+MNj151dfK1qglkGznC/LjWUAy2VsOsnfP71upmxEM8HyBe0jn/abrzSWesxzl7u9WSgn8eb6UT8ag91VcIv+lA8/vm+2VT++b4VSgx+xUda+QrQ0BgUHdUlK4LfmqGc3Cw1GsvgFqDUAl3yaKaGYxegzBJbQWkmlgmSZ2NE4HczpG3OsDWdVtscfj+DEhO2RNRMzMF3Hygxz1acmokF/PoDZUZXsJqZJfwmAGWW6YpYMzW+wuOADD1f7/FIWFpJybVR8NYzZyAoPacqfTSy8/ipA0ou4KcOKF7E93coHvGiDcXLqqfoxsco+O6MJBcMvKVC4azmBbTe1CDwjgilhsPaoHAe3n2gcMSeAcWLmseuxocgdg0oN/6ODWHIC333hUDkhuaBQGEtXfOGwgrN44DCOrb8DUWlGfRQ1MAWn6Goka0+Q1ETWzyGoma2egxFLWz5GGJT0KQDKKolK7ZQUGFLtlBUtuIKBfVkxRQKGsiSKRQ0knVOKGgi65RQ0EzWGaGghSw0IkEJvAK+VBGABWK/ykIW+qCgji2jBU7195Fg/Ocvh6gIxTi8DcX46nq3O61wjOfRfSXNoftKOgbdV+Ix6L4SFHTf3l3SQaLC47xwbhRx7hCnoPv2WivzlIc3YieRe0U07N5xWJxnAlbCuqxhZmrg1CGG9GuLhvXba3CXvk3MDps1HN9xWILmzL4E2S4VG3sKCpMYvzZqKL/jsPhaihXN+voT7igCFHIcrQjBJkiXQxyPoo0hx5GLAJWJu64E7A5S5rB+GbUOZ48hOuGg6dElnrpjyHW4o0hGuLBWyoIg/xISG4RQFSE64RhPOw/pEI/mhZ/DBCb0Orw9hv6El7W6GN4dQ6+DIgXjUVmofKPJkYB/mSebzeXTP4D+8sfQkljU2+DcQyv3JtTOl5XaDcEcQd0j2CPIVARSBr934gy431wjgD+CUkgIR1CsCHGlDkRIR1D3IIpv+GYckMnQu8cRRTb84EGU2PBTVpS1ahDRTXRXIkpojJxcOIaQRkQmhO3txES5jLjMEOUy4vIG8XC7t3OiNEbcVYnSGHE3T9C06B3HEzsteksnU//C106iAEa8CiVoWvSuqUS5i3gEI+pdxKNfQqaF9JY8orpFvHES5S3iTTeTR6fu23hGzk7dWkLGpwXxHk7Qc5laAgT27hoi4tOCqJzkzBe1qmJOufCB3ICVOwT/jxR8Ci5QJCORmyI4E2ekvVMIVs9IEah4PNZIp6jg7AY77HtClGvY97go17DrcVbPsOdxVs+o45cM3FEsN4yFj3o7jCVzbDqN430651Jue+6fJrC20+0Og4e9H4aCR30choJHfR6Ggkf9UGvPWnjU22HHW3jU22HPW1yNbtj1OIPTDvvewmu9jPseH/PjvocHvYz7PpG0bSnfjfFeJ29b/gxlEP6p4kRlR5zRjk6aZELQzTJVcQmootuSStpLNRKSbpYpkwuuzIBRS8V1v0ygMT1QapHG9EBhkxLPZEc8UwBgNSSaohghpKVUqRy3w4PYp4+go3qLndAgISg1R4OEoLCeQUXZEQMVxfVAqUUa1wOFTUpYVKPxmcFF2VlsVMgxkucSQWEtA4yyIw6qUm9UBkxUFCgEhfUMMqrR4nAcV9B4DFtQjJTa1dTiiajdVdWXY3iLElVxwlw0WAIaZUdsVEAQp76mAgI+wa3VXl0yU4FMXT/TjnIPRFnt6mrSLNVNdwkL6Qj6pku2ak8y+S7eB5KoqpRMZUipDyvXShJqd2UhWKf4rhbdSrVKhl+K7+ExzJebtFBpvDv4Y1KBdBrB8nydT4Y/iq/skFZ1d2En6uGERbbM16+0kFZ1916Q/BHUPhleKHHEw8ri3RNeSjqkTSNaPobkJ0EAJW4E2RBQm3pzGb4nvnoSfE/iBglVxvs3vkwjRqDEaMQIFDUSYJtGc5MObdOIllkACtTMwgJQkKjFEHCbenOLZREoUGLCIlCgqI7A2zSaS6NGoMRo1AgUNeoAN42WJwJx0wiRWRAK1Ex8agi8jIoxBOTG1gmFxNSIeGLE1Eh4VMfVzSTPKpu9oOWjdTNZVO+pSt8bCDSLCXQxDlBSFhPpsBYJm5hHE8lKgWZh9KUltPVChVOUlqgVaBarQJc1KtWC+WILLHMqVjAtXrGdriQUo8WOWuiJCh3UwsCD6JrJRaKYBiWXeFReM7lM1L2g5AoP82slh2lIW1hEWlAR6UeF/WoQ4cGHzRY6ooYEtdDzaMZmcgwRHkou0vDIZm5proC9gGLSjzrv1RiFxmy22ucMXjtBmucsjQFtpiZT9evFOaznOzregstIb0ZT0hEFFqh5kUa5NlNLU/XrhbC8NvBRkaW82/vIVYEPtuCCtNpbFkTb+h5eZqrHi8cmQu986j2L6m22LcC1EahtkcUINxNLMwXoxdOI42ZiOB0XSWzpf90ZDY9bXDWGpTHQrdYFmSpiL7j9tR1NQ0gU2uLLIu6CbUezCGKjEwdYwgx7NI9Cxp+zodQKNl5TZ7xGQ2PQW82LFn9wRpoXhYa0N1MjHoeh1DyNkG+mFqCPKNL7iBF/voWal2jQfrN5GX9qhVLjoe+AVLsw6sz3YR0SlrXPkNjWp5ckxKsS1GqnQ2030mOehKD0AvGMAwWMxNMLFFCJ6G10IAPohdIrxFsHEjAb4n0CCmjnOsIIS0TfdAwhBCq3b/BVBjKIxh1hJAcVrLDR2IjfwqHk5trLCFN7x4MWWkPrJuy4mlDYezRAahWiHm/hPapMtZ+R4jT4vIbEpYdvxFBqYaaVjZQI3z6hcHPdZ6RkFSis8SUKfuEDknNQBR7f6ZyxKphPQwxV8IsMlBxx+4DieRX2o9HYgN8doOQIORMoXqJP5ojOKFWfjnjYQpe9IR1dQ4vjQ2EtTX+FwgpNXYXCOpq6CoX1NE8UChtonigUlheCh8LyQvBQ2EzzM6GwheZnQrrS5hh+bY5SicdnmcgxbOCcuGMYqzlKOR6fZQxLHp9kDEken2OMljw+xSQfwQXOMeR5fII5cwQXOOfsEZzVHEOmx2cXxaXHox7BBc0tqvLo1Th8GyMDHUGuJ/o1zXdXc0SxnhitZb67miOq9vgaQMjU48sVQb/HV1aCfI9vAt4fwQfO+bDasq0WNbKULShqYilbUNTM8q2gqIXlW0HeG4YlS0FRLUuWgqIKy02CojqWmwRF9SyxCIoaWGIRFDWyrCAoamJZQVBUmoSjNoS8YXbgtpBRbwv5oiGkL+SDksHSFHL8Z68OPE9/7iZ6SroHXczx0z+izfBAWPow11vSVrP0K60l4abTRi9ATPYIisRMwCdyxBeC5DgcIcxX8DGE6XsYdgyZuZ6Tj5x4xnISIRFpRW0c/ysbnltl2dseIilJzzI7sKlsjzHDiEkydDVRS/xk+lc2CoqZLfASAXWO0JbydrKnZY+Zwe4FSG6OHKV2qqVlI8/+icl/ePen7f7s3R++Xr3bvPP/6g8HqNPt2cntt+8fmtJRDk0TLbHh9VcmLwua08u680pv1VOsnRNPKPj0OYZsWOZXC+KgoqKg/D2eVeC2E7VTPE/a11Eme2XDQcM6epSss85u5Mks8P/p6+npu9P92W64uue3Xd2HGn/YLUWOsV24IyyofopbRZi8kcUjLJ/pCEtdPsJ2VI6hYmmOsCTbIyzJcoQ9zh1jRfbH2DvC2iV51TJv1i/z/3l7+hO2zJe/70O86ZjfHHltn7QD+SNsFm+3zPc2n7TyJTT+3Z3f8c3izR8b5QhL+zFO8Pi25o+wBYVjbEHxGFtQOsIWlN9kC3r3Xw//52r/afdpuBlZ8/dUh3vbl6g1K7GbIidO6ddbbbmAgXFZ9dt2/B4FlnSEjSXDW73limxr9OrNbCn+475UoQNLU5r5P/HtasI705/2nz6d7oh7iLX6pX97dbX78vHwM583X7YnPx9+biPsBnCy352d7DYX25NfNpaEYzz7Y9GBMTAdUbewvF4tJHpxsbtUiJ8SA/5evTlOrZgIbnJOzCOHW5TzgqpIDzCnyEfz+JWbUIaz4+VSkaiFJrkhUSk2msPDWprkBoUVmjYGhXU0yQ0K62naGBQ20CQ3KGykaWNQ2EST3KCwmaaNQWELTXJDwi7QHT9tr643gONdQNRiaTIalKzQ1DkoLE9Gg8J6mjoHhQ2Md11DL4nls0GJJZp8B4XNhNtcvbmFpcRBZx3D0vegqJalxEFRhaXvQVEdYVlX/TLO0zBmJK/AEgChqHQVFYpKvzZAUZE501shCP4Y3lZa9RUKaklOHhRUWOg2EtThrnTVj0JwxfClgaCK4XPFR9xHrt7URLICoaxYHz4oaCFZgUhQghGGby2Qbau1na/CkL/wZYEhf+HrAkH+Ik6gBPmLONJharK9pYEgehF3jkCTKKGohSUmQg8vNIkSigrNmd5DDmHmSty0o2NpmFBURAlNeitEpMmRUF6RJXJCUVP/6bl9p7wRvDuE3t+9x16en20+77aXm99+3u1Oq2WrrBPwrANmFuK0aN6mkvert/LXb5AGdFW5X+mq0l+0OO1DtGrbF+K0YNuTrumgd0XotZwUUHsIVm94YBvudA2P5O8ohxY797zuZzDzl+74JW0veqN3IWyLtVo3eDPmhlQ6rWYFbkuv1Y5s9cPZkW22Z39HN6gyOxUfdmD2dyImvB9735E1pu1tPAttXKzhovyQmOPAQ9Gn2vBiyGnrOg1fKOKij+SAyvxCEncY1eFRHfM8XEUMQYK4m56FcSG8lhoRIv6CVQ+Q8NeWeoCMv2HUA5TVluuYgK3vhmBuMI0QQlwLGiGcDiIxwR329nxO4yOs4VEHEHMk0BAJKGyE1xLpGLXYhQ7uME7uxck0fgFqJu/LAQG0DY1ggMJaGm4BhcX3Del461kKW4CbaVNcHtyKnMIW4DB6CvZNQP4TjYSAwmYatwGFLfhJpLd6UNK2+Kyn0AT4rKfQBPisp9AE+KxfoAnAM2jjGwX21NmIE2ksBtTMRCNHoLB5rq2sZTAF+ERkMAUWXzacnWo6axk4AT4fGdVafPFgIAb4bGQgBvjS4eJUi1rLgAvwuUQI1m7wie/KMah33hyDe+ctfDlFPpM/BpfPO5ojAgT1JNgCChpIsAUUNM60ubUEHgFfRDyr/AwFLTNtdC2BRMBXekKaFl88CXACviET2AR8S8J8bS3+jWi1ZyjLyMIyoKi02jMUNbOwDChqwV/fkO/EQBTwVSRaFqQBRRUWpAFFdSxIA4rq8SdO6DsFFrIBZRlZyAYUNeFvs1DbMwvggLIsLIADiUrY6RIPTcmyStxQVOgNHTYMtwmfScTTYvKssjcUlTjg4WtJIkBB+FqSoPmEryWJmE/4WpKI+YSvJQt4AlSkmVejec3brdRpNAzeV2GkUe7JBIwcX6EyDVe6/1wswsxm1mTbPRjCVqN5naN4Ixr+5HgfB5AUsjnSfesrffuQS/Unks65vNENmcbtVQbDK4GUyg8VJbCRHnUsyuLhHb3eQQzMIsKCCUWUcMk2akXqneF0SMdGZ3gd0pHOOqhQio2kow6lSCedVChF+meyCqVI/4wKYlgX0zM6iCGZsrBOyKWXsujwgXTOTocPpH/H6/CB9O8EFbav8RGiDttHJ51UuLxG0jQgMeiSZjdU0X1RS26nvTOcWAwF/HDkqjuy81gTSB3A4VGrbu+xGtXTUA7E4Z4Cnli8DwjEVtW0vW6rkUDKRvejZ4r30QhSiIo70mFi8CI2FM8yFI96I0Xwgi2Uk4MLi1A4T9AfGi0McB0NyggnBkPhEsF0aDQw48UYKCWiaIDEc4ZhAdTb6AgMMJQTAQiG4jkG8N9oo8cfWKGcAv4QCMXD2BzO9NqYdCjquP6F7u4kcn52OIuc7C9Pvu6vKbU5BjNy3wWQcE+h0dSQcI+hYcZQWEuDoqGwQsOMobCOBkVDYT2NOobC8qc0KGykcbxQ2ETjeKGwmcbfQmELjb+FdKwMjb+FwloafwuF5bXHoLCOxrtCYXntMShsoJXSoLC0HhkUldcjg8JmFvwKRS0s+BWJSiBONvhUIBAnG3zeEoiTDT4RCMTJBp+1kZZ2hqIGFqwKRWWdyaCgiUSXQkEziS6FghYSXQopHRoSDgoFtSQcFAoqJH4TCupI/CYU1JP4TShoIPGbUNDI4i2hqInFW0JRM4u3hKIWFm8JCX3S+EgoKo2PhKLS+EgoKo2PhKJ6Fs8IRQ0snhGKGln8IRQ1sfhDKGpm8YdQ1MLiD5GoxbB4QSiqZfGCUFRh8YJQVMfiBaGonkXiRc6QI3x498+7T+/+3bs/Xu52Z+/+fFdFHBtxiN6I4/DTOhvAmz8ktUbw+mJWmP/VE4qQW9IoH6/w+avnI5184CqhgdNJ/XR62WQ0G7xzSj+b2FUVefrb87PPm5+3hxH7qZNVgbNaFM+x0PjofFlSB2yNzJPR4si86mVpHYhuieheG91+Q2yYgrZrsPCRhLr1FE5YaGiPS581+K96KBJK1lFzEN3sMghKQTW5DGUQAQ9NA4z7RV0ejgsNSWHc+RbyBOPAgQ/8uieGR5P748i7f9x+vBqfSdybmoPVS5i8T2TPLE8+3It4pv5hp4s4iZ0QTqcpmr6t89XyD2Fm2mo5OCjtzfoyqF9lsWXgmLTRMRKUfcF8GTPOsLBHErUkKth3BidhqOXwVmNAk96kWRyB2guBtd11gDIwfhSnX+uM1TNiobywpBcoa0yP+icZkEqwMBR8BN3e2yOebD+e7t6vtL+q65XH/gEHjRqAqDSR5VGdetEj1V2wd+AZtSDhDaAv1xEAVgXeE/g22eGZL0I7/oeAp5qQiOYhIoIqg3yFmRQL7+OMdKYzOr1vxdB18ORzcLc4+Jkqwv6jjqWjZMX65uApR1iruMAGDVPttZ5W4nWGWl2vG0dLiqx30urthwwm0faUsb3Cm7HKMGFwh11TBwZp2PWa8F55ZhBmTnmWMfkwzuo5R/qs4MZbrU/s/g0wbHxmgyKZ4tqjrhp0pX/WpjcyGSxhb2BS4MFqK/0646yNrX5lv844y+BBAxsUaT88dQQejow+VW/gZDZMfdwU8tzhKaMsLKajbLKoswy17kbSk8SaTs9Gx55hHOWfhZ49/EoXLdsbhQz0r7cMEmi/7v4cM3/0kIEFFhyoWuRIhj962IHNFRxIBg5XffKl7QVx9KHCjtfG5HUPEIa/xKXAHjWQ/KPu8bzexYk9tyCl+UyfN+zAtAqNIwPDKvSEAYhiZUtHFSCqsEcMJCirogu135PnFmBsM2i8HsCDBYs3xo3O3k2xUOSsKjnUs2ZPWwi8BaTvmk5euC6SxVFAhffyrSfn6Dgj0yn0jDGynkLjyMCACjci6PtQoXFGdlToAWPkSoXGMSNvKuqxxTJnfmus7rHFjJyswHORRUqpbLHEIEE9GRTKlGYYNYB3kZfVH9hNgScBRHPT5JVWAn6l95SBgxJleNxPgPCdsj1/KN6WYGAzBe2qpMcUFNNyBlPMGwa3nLEQxEdk1sBkCjsX2LkWU92l1hY2zmpPqd62x5hIdbe9RRUeAYLVdRKFr3jIt4ErFHq6kNW2UBbWaJXIIN4afZUmClRKJiEIjd4qLHTOce5PMJRBIOCzsywqD8pX2FOAQ0yL6KoGFJVmbAsSlS1rQEEjGdRxVlAosAMcWmylA8qWhTwClZ2lFxTxVFBfRzwJMqkvI57FOELNZDCOjdaxqBHhbJ4A4GQjMRok0uh4uqDRiJNJQS77JGe5UpDr/qjJGxsvCu5gzu7Ncw6KnRGBpS2dqACQn0fMmIQB+QUkoiNAflCKnt4LsM4M3Mj6O5gMCwgBlrJ9+5RZEUCZpQGodFIP5CJpvrNmYcsSJNAL5d9J4pH0cbHfc4D0KWXxwz3P/R/u2g2Qyvybkspe2XE8fZzxHz8/XD39rSPIaD0t0fvyRkS4aIPDcOjFwt+/7wtn4lfy0R7iAKwDFq3peylGVk8NSjHRRDwkKv7UIb0mF5rQhyRneYQmFNbSIAAkKg/fhMI6De+78ZkI9XumP2lYARQ1EgfQ1ohHHicSyyGEss9E9nGUffs4XUjlNih54jk9E1Etqd3WGMQidBwkO0cLgW6keuGnntsfIjlE/lNn9nST5qL8VPlbO6C2Adfp0mgHQKHDMXKB6KxMykc8bppAyYIiwD0GbnZNe21yhsUPCvD65yxtdgYOpKpjjePna/Vhj3jzL7356TTzs1ovdEERqVoxdJGeBIjGVWLcS1qpZeXZ2AyYbsTUgdRMPD9ZADgM/ohviaBPU+K38/NPu7PNyc+7q84p7Cbmx+1l/TODj/mu9128pydXXRot0HHMgPEGTy2bv/VZbnik8q1PbeM24ENii3Wzuh+tI72Fxe/grDc0agHuzxYc0I+KeKU3ooMoVonb1o9OG8Ep977n3/Duvav7+YInF46XXy/2KXLMbbA+nhfv2tAqdJtg+5sldubX5yvBiivd1hXFvK8qsTAC+Y+Rqm9gEdf8KI8ZDQdF5PUxATmD6KjDQ6PBXjORE7Ldx8A+XUGtprU0kaCJ2aAbPclPi7iSFVd6kyJpJoVfzYp7iBQGtDh0IgCl0MTKqZVujh7dKfNjmM6qS3DgYvcjRPpd3SKP1kl3eWgkmemXdShJGqgHhc2GXqCqrc40Lg9LT3gaIRLWsQuop8hwwDZiG9kCm8qCIdefnQaZnDnyQkmWfwTOSXcorI+2zJYIoFFR+Md8zfcjFOyJqbLg1sGP+YrvWBBUUH4Q6qp/vuI02r6tYJ6tA0C9GdiaBRQ10ueasJqK97AKuNVkvIdIMqDjodcGBATKk/PSY5aLu3DvEryk5qHPDQhOdMHR4573sY5xmlsLpI1uvPb94VmfvwSFrGLxld4oZmh8pTeIOR7fw3xoUAs1U6vBLizcNbdBZDKaEYMJ0lvLnqggUqFQd9IhbQ8dG41A9PNzY2RYRUkmfxuw8OBIZUTBY2wAWnllFr1SEEgM6QlgeykKrRuNpEjgB4SISktIQ1EdadOHRfWkoyAWNZBGfVjUyJ4d84jZB54Woewymx0UlX5bqy8YzrBx6n3nFC9rdZ1thsVnTU/V3GnOVhEytaFvK40MGbHo/CrBHg2OPko1MqRLLY2vqjhG1QXdnaLY0kAoGuo85kfMO3yQBegbekIdBFa+tFwJ348IefC49ZCfE32V8GuJeaU32DQV/SE1D47kRoQ5xjaqEW1RpB8UCRYaus1nyCWBDRTKE5LExhxr659iUZgfNNm+aHIY0NfAaQo1OahOyTKilUHBYnfEJLD7AjRgYFpAIDqv0KjiEXMJjFPvssjjketxaHxjIx8egVy/TEcFwLF+m45KyLF59Vb1akeq/hyLgkRkgGLizwIGcwrMmkcfZMeNRfdK+LrfX1HPKvwHs1KktHoXSixuEtEdSgwuTIhPmRwdGPuStBxuaHzIx4pQ9VcCpzSBaMstEAHYE3sivmMil/D6QpfoW3zDppPHxNRtrdh7e71Zmb+213emzN/aGw1jxQuR88CiqA/c9BotDOQxD0osEpexRl6JO0BBabGjvZEaewxreMiyY72eTWEPYY1s+JFeP4QV/hBWv+wWr7oQNoKxSufIm0OJ3CWzf2UouiJHo72ZK3k1ohTuStm9UYox5FIDfAMxdp2MV529KZpbap1radw65a56gn6+cpcQBW+HB+VJyI5HLYlJmqeAxhfL1EtAd1aLKdw+hox5qlYe+33aZpVaRLQpPQoiVUMIWXpCRhRRMs/tfUrognkjDPu02+gpvlZe1xux/NNuqAdScrXi8uKFPFqIZcFZiDesKPAkUA1KRFEagZxQeedZ+7rHx88VQrjOupp8Wr2/SbIW4FUplOmsMJ8w0oGxL5hUbxXPPmDvmUJoUn7COxuehak2CasxHfvQXF+KiCp87iyNNI2+EYaFbDUaxb8xp3ogHrCV64Ei85zQiJF0sNKEjCbyTpOBo9Si7I7lK7V06zbchngEqfcmTp33cIsJNdzcS409hdUHL82ab2TDnsIa2fCnsFIPxJ/C6rhO0VLm7bMH8FvJwM+77eXmt593u9Pq3kIX4Esv76A4f1mDbLKBBcRb4LFe6Fq873x+LUee/2hEld5VO6Ouk8QfvMBvFzVEq0YnJ+7EiQ2DrDrBKT5c4QpO0HeLRsNdaszgyJ66WnHYDaf+uSN77GqEYTecVqv4U1cdcC+R33Tq/BCJibxIW6AOLTGTnHAL1GIkKmSMMLqHJAoA2ehLvPr+YOGFtBrn1xsiKF96R3vSE0fYVkcG0kQKanLkLqsWqKkLXVNvTGi6qN7qObao3siHLqo38smQin+5fxGuE2JFU1Gvv7wSJfXczYi4iOeHhAD5RGKDKERYYrdIRNjEqldgYXXSd60vVVj1CihJohD/kB8Ult5B6q1mKvOOSM/RJpVQWE/ucI1Gs5ZPCC1Ziur60Uoxsf6PUIqsegQWtZBsOiSqM6xWBBbVkgeG6udxRkiqH5acI6l+WFRPlFpb7WVFIbDMWBJifbN2hiVctVrJini18uHpVnU1MGd5Ga+6boazVvnUF8nHB2dF99TXyNux186ISCwrhO8sQnN0NrAPiFC+Ufnmx3+9xL75AbU6R+ntC9PdhXynQ3pbjO6dju5sIS0Iob4WUb3T1acfXs5PRIYsFbiVXGDj1FdZYZmPrTj0PtRoF897rIu1OlHsRKmu7s69e9XTcQp0CyY36hzPPUEjM+TfVsPpEd+IQ4/4xtekR3wjH3rEN/LhR3xDwF5Bda8LFDuv1N8StlzjPKs7LEC1yXld0bHRr15ZdVT0htfco1tpB9VWrsiavcu0EqbfiRujl73LtPKh34nr+QT+JtPwBwkK/lXDH0R3U2nlxd5UECsKFzgaViO1wO+7ghAoXWA5wVijE/m6KUBdx+G0+furDWTigtfaDZ5qNNy1BurVaOkjEjYIcIV7ol8jc+xqjPvoudc+7NsE7kqEtTZqLm2tZrN7SCsOTWmsr7O0qH0jn0STGuv5KCTt6153LvEFx4Z7VqIrJpCXWPKs7SUWlq2gYFEjWfTAoiay6IFFzWR5AovKllKgqESNPhBRLVmtwKIKUa1ozMrMwocbc5Io3ieihSycuNVKYrZ0F7HMXuJb3ZXphBqBaNf2+3Dfyf+85punuN7UH/8WlX2o8fnNm85CMFsNd1zDy7SG3521zs8Op62TO896rgM8l/jE0br99Ov27GT3SZc3vRA1Phy/EB0C/eWHu7b8+P7j6dfdxeX+7Oa/P90e/vDwb+nDu388/7y/ut6fXL370+FA/O7f/9/by9O/vfvj4Yf+w+E//PXQQ3drbE4i2RaTbg7vD02+1Qh4Cnz49f3Nt4H69PU7kXu/CH0TeTSg/vXicnd19Vre6SGIAEEOA+nkl6WK/MMfu5u+u/0I+91di15KK6jevDaSnlMFd4dx9eX809fTXRW/yd2CNoJUhmgJOqCcx1tnAkEDO+ShqLzLk3jAQIn2T0Ki8mZPSNRCux4hURkYhSPCWvZyhYUVVkIeispKhWBRWa0QLGqgLHPrOrSRVY6HMku8U9KruIjkHcvFfppjVa0d7h3spksBdRzuySr0vpYoPJza/doh0dM7DDAoCDuBRETlNy4kqmLnqkNUIx+o/uk1etGC4MOV/km3D7aP56DK39rq0KI4R6XRDuC13bCWko1bNm39hHS6E6UdfT1HjRtBu087IA+t79NwrEi9bMZqur5sVL3Awzpatt6iVYaFrccczsX6tqFNxStHEluhMeuN8r40+vau6hvP61pXqyGE1MJi1awFol2i6l+aUFt4TKgeSGESVUcvKCQXNvWakVdKj2yQyqzP9FIMELZ90SyekCBzoDWpEcRSYDWpoaBCrj+mt/7gGA+m3fxBrzrag+JoVw9EO4HUJ01IfEL1QBpDwSpiMSiccOqIpqi253z2zFZdTWo/xzt1ZuDBSXlca3SJ48wa60FgB3a/aGdzxvIeCbb2me7e5PtfKOp8nqvjNCbK/7Hekzq5kRctv3uGRtrPy7RXm06gPPpzPdEnrXo/Jv6k1QjkFItPlWqQNB4iVeg1rqDwaF4MPGSnSK9WgEhbShpHD0i1ManMQhKAjkuFddoA9N+y4U41t53QXCMXSA4sWupGI9GAG4QzlHkr2+rEyfwlpRGIN3Cuzj8exdEKpPFvrr7sZc1pq/q0lwupovHKS7728FyMCrbdaG1BB/9jir43+hlBhbgIOKqLkUer6uconq4EQrkFuhQIhaWfs6DBk+gKI5RspiuMUNjCmosDXWANJZ9oG+kiVSyDTi6DzC1rGJLfMO+2LRQhxECMEWs8p7gKfk2qWFpfGayhq6VYixXlUt1AYy0Rsa4tfFVSM96s4ewhB9OEwCUk4mNauniKheWPdvUit1Wc7RqR+MNdfVpZxemuEUlzvKubOlnN+a7ux0S7K2ysIAAY9dvas5JM77XC4toLjwc/BywZImx5ttG1C1ABV3HAetgrH/4ayapf2ewrK8X+V+MPhY2ME3uygr5+Vim+g9+s6B4Y6x3glFXP51/sFUi18kOW3ZqRnnZCQppa3cBOs1z/YtWN2HkNq6+ZagCoMflD6AehazuNXcTxxZ1WpMxHajROU92pu3x6owhVdz/0fOkGcWn1onmyxAx3vQZ4YyHLXe8VXr5g1oG+tjW+WGSfMBHPZZ/oqdcYm4oJ04jE12zqPRYURZtGJJ6cu8n1SDw5d1PqkbjXt0YQUrz0EGa8DQaWiLvJCPw6shMJispqmmJRM/P80fg0LO0WyiyyCqZYVJZ2i0VlBU2xqDShB4rK0nKxqPSdv77qRP7K3whEbxf1wR353aIRSHG6inVHd8XhKtUjPc2Gi8ONZHN9vrk7ULSv8UCh2SZRPkCE5eXogQaIGFXaBThg2JJYbUmdr+KfhW1n3uhfVs50E5GkIonx6Zav7QIXMOw6IQZBVnlmt3qyKJ9IqkNq7MRpFxiBUa8YfDxlOxpPttcLWWV+3QrmONxCfxRlD/eXxwdR1gnQ1b96z77TZvbSEpH08alFLEoLhEF9EIXuZy/sqaK+CxXWQLSRT6FxZo18eJxZIxBdUWk0jC+oNAIpEP0NIqMC0V8vqJTE7myA0a0tGhyZh143igbPDzkLGxrOj5g2GxbOjxgJG17mF+teMbzRFdi7nlyXoc4Numdrj/QxY2CdX/VEr4/pK02demr4K00jEP3+1WAv889fjUCK16+6oZZVvH7VvbCsI8/BuZsXbiqaHrIac9EXFfvBjeKB5Ns7C4ql3Kpb/caWGBthcHaYJTqsoB1mX3RYqPLqDQf0GkSzzJtevdtYG4TYG7S4DULAP4F4DvXTH7K0GUKrqYrV3yH7oCjYYPUMM01Pq8cpdEL1oeZYXdJWHH7xr/vHOsXaX7d3dE5pG/QK1oE8gomjNwcLTDSnkL+GHKed1pbnVe+M33OEpeU/4ozrH5YVw0YMrnF6/iP+B/h83qjejOrt9uStBHHG9vylBBpetOdCaIyu3ruReM/ic5Bvxt5PEt7brNNCfY2kjRZaA4q+izTyYa8i9TCB3ovqzSLK8P0dRFGFr2/XgWdPNjLyrEUbMCo1FguYT3aIvLMRFjiR7mOIuhQtGV//ROwTb/1LR1owvh6GnguNOMJ6qdTDODadRhyeRNxQAuOfd+sH4BjJ4x9yzVrU0aGngS63QmJmXgYazWSPSUgziZq6eWznaDony70S+O61niDSC54iq2sJBfXEA0T9I6dAU58UxCdJLHELan7ijoTQ+MzUa0d/FiaemKUQi8xsgbDhLM/SsZBPlFkyVn2U0rz6RhPpSmEjDgvCarSKxmA14vCkq1gPxFOu6vWBXFRv8fVgRUmzeqYPA73rFEtu7Ij+bFEA4BFTYSmOfBsBME9SvOr5pvHllHyrZ18OeHPCOfi9QUuU2i3+9TP3kAOgXaQUzUtT3RWBItkLPDid0an7v9D2b78HOaMqnDQ6gaybAB/eLerpzNMSYj9g2BpK3Qd6UUAfexg1Oo6tlDcyoR+nGnHYx6l6q/gyeSMOXyip+xcriuR1A2PLmn8CMDlnvY4CnIGhbhXvVZhf9qJ2znBgM9IhiV5TwZzZZ6vGcCqcKUpCBLXZS0h9gAoLUmwYytMYxUYc9t7RaBV972jE4V+w6ow6JzxAsW6YvCiXc5VXy1pXS1ZpAbQ6oDAPZPW209z0h7oq3XacnP7wrcYHFFYDX3rd6Rgb3EZvkrJDUBt1sHbFB2Krfa1+ZMt9jb5ksSetdGjsSSOQZ0scDbt4usTRiMMfqOqqAM4ryn11u1/vVXqBrWisYCqi7eE8K5iKMM6dVwim3qYLGGYogO6QQIDzhXx9sZBNO+moDvVv4B3Vwe4NPKQE611WwR7rXJXOSWOCBdJfHVFicYHeRuqrUmC3kVYj6X2kkQ+/jzS83tl9pNEwvlbeCqTYSer+xlGxk9S9bKInNcMQKSPHlM/LQ0PHQ54goj9kC4VNrMAtFpaokCQiLHUbaXx2hq7u8dwIffqH/KCwQkqkQYOU0a4XIlnP6uZiYQN3rcO6IJIIC0T+0qVEgiKwqJlERWBRC3EZbUymzKqiQJkRNXZPRCVF5qBhRJTcI5GqJwVcsKhsQaSxBRMF+URkRx/GGkOSPow1WskfxuqBCn0Yqzes8IexRiDFYaxeSVoK2TdrY5tyXxyzDcd5r3wbTezzUwm6t4NG4yN7fmy0P+lebBvRsvK1le/Nwh5Nqhl7nLBuumGs6qG2EUx0L6JsJ3q8ph67+eru7LEejN4nGnGYynmrYWzpvJULXTtvBWKL542G8dXzViC+fF532PKK+nndvcbTBXTEJslbmuoBRdVwPTLy7udtVIRGbKg8zkZ/uGgB1WlvSRQW1r+Ff/zEuld4UBbWu7iaPNG5uJZ8IoKymoyNqU+U3nNv4gsL+W3lQ2N+W4Ho/aPRMH7/aATidRcbdsiO112sOxl6Z3UncGELwP5VNb3/gtdI1+mOuIp0veoYqPghxvan1S1sVaQ1ruiLeCMf9iLeyoe+iDcC0dX1RsP48norkIJNW3d9V9TXG9boOIH8/ugGeaLTdXYRJGpU3ZxbLWc15LAcFaV1EeSUQpfWBait+mA01+hGl7Im8FCPKqrpWIey1XSsPz137MOCsjf0xrJBF9Jb35k9ZbXyoU9ZrUDsLb3RsEjf0luB+Fu6+Hokxabh6pGc6jm0Fc0zR7tWkEC+KQpQpPFM8Vwe+n68EEVSEg7LNZN1SSxV5taBhyVq6US/EqV0JlchjtiN4UnUzT2RmSeLqFjUwL0sQ6OTqJpHItVEljuxqFnzJt768oWseUIp0pz0xp5AFMxzr5WZJoS0EmIZIa04NCWk1TL2YaoVh2eESL2QoKCjS70CkGmZnkZGhajXNHIp/IGpngytw96Kww/pRsvoId2I8zSkT87Prq63Z9ebk/MvH/dn2+vzy87Hv3kPPvzF9eX56V8/7n7e/ro//OeH/+an/enhS129//G//dv7q/3ns+3pzb8+3Haud1/eP/7g7VPUxen2evf+NtjXs+vbPXR/uEfddMC3v9RTDlzK+c0TjlzCE7t4++nX7dnJ7tPmZH958nV/zeWduLzLtLTv9sbzs8PuOEz8Lz/c/fGP7z+eft1dXO7PbubA6fYwGQ7/lj68+8fzz/ur6/3J1bs/Hfbdd//+Hw/f790fDz/0Hw7/3a+HjO52xJxEsi0m3Vx5H37gdp98invId3/TFVDX155oF6FvIg9CXB4+3G+HTXvxZwL82fZyf/3zl931Te89fa6nGA6I8Wl3sv+0u6wH8Dedfjta9ru7vnhpKKC17PXLp+raoKtJK5CcYIsbiKMmMA7xTvcs0rdhahr4ONXTA6hDLr1c2MLvKwPeWq8/HSC+7D7tv34ZvgTYXoqFtIp4mWHVi8PQbqANk3M6Tt3nVvgxUXftdHyghs+pdgkwyyWgugxVTBaVS4BBzPsmGb8mOk79C2VyTTJdZ6pCf+9cd7ug45S6I4Fy2DyDurU2wIpOO7t+FUSqmg0K6FOIJ5exggjLkjEB8Q+hp0t9HCTeJbaqaUYPy7rIGj9N6mIQRrdQQYoubI815CrYMHU4MnlySr1FyvEnpzr2nT851VHNSmn+Wy+Lx0VqcbivsGGos1k9S+0xqoHkZsdFA0HLhqm/xFn6S9Yrc/y5qf4QRy/5ULFet1g4pGpNrvxIGZhe+eufhF75G0+j3ALkujc3evmvHt0Cf0iqHiWD8pD07GT9+XJ36JTWAhTogxGAu8ERDXUHj9qnCbpJAhz8CVSDdL45f32ohknUcO6e+QN9KKo/Q9CTotqyaJitrprJAs1A7XT1YMJWvMeDlFAJ6DXTs4VoIDN2kNe/YWSHQnVXZ+j/vi2tHJWcs/D87fHi4ubdYfvxtLpA4jr7BpUxT6SMDPB+mSytil+Lwt4Jqp8l0U9L1ZtFoi8D1YNoCtMlfhO71NfbR0+C6s030Yt89TqodZYvy7l0cXn++XL75cvNXNpcXey2v9TlgTO1FVTzzaTmXj2I6PaTejB27a9+TgJ50Osd+gJcf4PM9HpffeTJbPUAeHxboA+Y8yASmb0B1/uusDfgatcVS50HS+88WOhLcKPQQi/w9Zfy4kkGNmAat8AMUFO6kSGt1lLvMFqepZ5NZsPUsykkEmQhdvIdy/21YhyvoVSvxi1I+1AXmGk9oEOWWMNeVloNd1zDZVrD78/ZiqazO+JN0/tYkfzh3T/cDb93/2V3/dv55S/v/vzz+cUYJZLeFiVy/vH8pvUkSuT0HhZzt3NsLg6Z7H/dbQ6HtV9vSq4kXuTq68fDsLnt9CPiRB7cIDPJaBRWCWyD8J0VtXKERU1yDjYI712BGQl9zAgcJ/ZxI9yHf4YPWgz8DoYEfGqMfewIdmowvWAEaGSBllmHGukjgDSwET+AjaCDFqhS0B4VgiO7LHz/FwIWxBrmtb4L+trrEdwZYUDxOO7qEJYyCRwkipkgA7jIOkydiHJBelZTebGlbg9D5fmO2sOKdJ88ugi3BTaEW6YaBrnKWWenokO6I5GAh/THT6aNNDcWc0Au9BAvA8TIKmAVAQ/ZuB7+zM2CGjpHrsAAoslpdwyDKLKzY64MwCPo0K13H7tHdGuCjt8j0kqYSBco5fkdIk8CiuQBUITaH1Jne7g6XK23n3fvV+FJBEbmeeXcSBScBBzT9Y9OT408QJBAM6OLPfP8xGgQreZgzwI/MeIAQ7IKGRmEOsDU2+R055d6MOUwBwp8gR7mYSUkpPsFE392QVxACXhId6DyByA3wIesguhF/gDkV+JDumDU6HQ7iuvsKJe7/+/r4f+tXzWicmJ4CkVChQbeACI9WdxKaEn34yvuCx65LizgJNCe1YUrJsX1+ts69EgXr5j451eZgyBpxPGqqde76ldez1dATgQG3abIPmMBMfnL9UoESvej0/tINZtspqAVM72LmAHCZA2geIEtYdZd4LKbdZsFMLoye4iyKxEovc/A3RS6N+jMbw2IqVqmh79biTrpQSsLPfzrUAOZgkAuuiPUsxrS4bS0O73/7H0sCnC9qWcZVLcbGQBPmGkJnLMKu+i7lVCU3vAo5A3ErYWFdAbZEhQCjvmwFqvRA64usRpgPn4tgKIH2raGFclDqnMmkkEDEjSpZg+ULzvwG9+2kGHqn2RRaUb2tX710NLjP9WTkimIZGvp8V+vxls/BY9ubVDtQs9ehj/utie3yJlKeN2SD7zaWsuu+Y0Pwo79Rj9yV+Duq63lK8ylDpCwU5DOVugrcK7HcVPYAVZYlBEgUWAlMIelRkdF1WmpESxxKCDAnd5K5m7cQGXGCrvs15vrWPvD+tjgS8UNuAhfKm4Fopf8lrqNn0OAsE636FvLcMwsXStu9R+70LdaTRfDGvgmvkzcCMTXiRsCSnyhuBVIVyl+jmx8hqat/Ab77rORqp15HXTnlcEhRB/5Bgqh7ui6cWsw0dOkMQT4aVK/DvBF40YgvmrcwHnyZeNWINpJGgJ16h5EsdisLnbrWwTepLgaJ1Ln5QFGlPBMePJwqwaiiaetQPzDf/1iwVeQW4Gs6mzaiibsOKj3EyEu0O8mVgn40f5pNQno7hGW5wDFoKOsle9A2PphPfVJWs2O9Myon/R5wYVWoMx9iHlDB6YO/jBHK7n5Tdgr3E1PDulo/3n/+ed3/7I7+RkkouU3JaKdfT05vaFhXe62J88FgxE+2smhSy/3P339vCOpZz/vtteb3b+eHDb7zzulTPEEeg8HhGu4eE/i4yiP8BBzJsygbr0eKh2q2XTyj5peY7tMs3UUlDKL1GWOwsWydFTksimTiEB8UaDVe145vJ+/7CzXsh73bKVac5yl1py0CwYg+6bhn5m1/DMH8s9WUZxin5IGIrIMwJsScsWyvRVL3BwKivhJ4tFhknh0nMO5kjSHfCJ5Amnk9nRzcfP/77PMkEPHiGFGnYLW0sy6JCmZwgRxbpJss59DlnBBucwi+s8sYuIl4yH2SWbraGF5DmtlUTeYzmEyStaQXUc76xKiZApTxLs5lAXv53CYPIsY2gTEVFI5tyDzvkkksDyHg+KLbmPxrzaW5bW5xzXDjjIB4FDZKSyU2KeiQTO4e6sLbg7Xy0/hnYQwh/kQ4hwyFV8taMTJc5ghgTsHyYBoRp2DZCXbrEtbkikciuim0A2in8MHispjkJlKF+tSn2KawvOIeT3T6Op6t/2yuf56+fHwNwPu2GQWSDI6fkRYRyZzc7hkne/CM8nsHC1iWadFvKnSbOIqYtijsvo4ZppCxUp5Cokk6Q5Bz/izrXt1Jk8+CO+JfBcCvkeWKVQqN4OzkeewCXiFYjegh1E7DGJOmKYQsnKewuLIhRtVAJm7sLQZv5Ip5mGOktDsiHFMN4X3NIdLUMIUilCJUxgSJU0hPpW8nh8BSHwW6goQRowx5goQRrwxaGpGxHZUyFkV11LIukw0P4X4YcIc7lScQ40waQ5PyeQ5lA9TJvGvyDMNIFVrrV1FhogDntiYSO0eUx1KbDDEsd74IHhjXRpboMkawPeIk/gNNs3hPfG131ZCZRKhy+jg+YbcjixbB0bKwBb3qxXU1mJJKUPn222uI+UCK+xEaXyxQIZpjCBae7QgHyRNoo3wTrUNJAcvPNpC1JhZNDDLnMxa2YgOey0jQhk2OYELPkMu6/dXmENDcXESV8OlSWytN+GPdUkU3kyiDtk5pI5FxZdCtIfv4sRxZAy1dzoGQvgeDAQEz392crkjsfzeq5Dz4ftYzgwB6v/lDscLwtPLm8LTP+4/v9g0OID6/WMxA02/+Prl4v/dnvzyHUDp4taD0usxFKp7diYoXezRQOnyDLV7uf20nQlFRxInoehiuxpkfH22zIGii5kDRRdDQdHhGm/5Ngd93minm+FjdFhW3/cR6aBsJHKTpKWMG11Iy7M2ejBpvJRWgs3941fog81JSDzwyKUxQElrDVBcr7nsywJkZSVOibNHutCTkPjcWzmFnw/p2xyDk7wWgt417JI8ybFLYWhSz8gpxn4cYM1XMNFe5NAHo0+gCTo3gyfoeXuzobQRb3DSSI6fA+HbSvB5lw3p8iRqnStH5FX6o/D2vJ1EjPMKqp5fiUbvx/FzzNZ8mESi83ESE9Qfk43nZ7nB+TKJuxnMFMrlz7sv+5Pt7SPOWd3YgmbmAQLsC9D5dCrhAoNOsv5kChS98bnCHA83BRa9rMWid9m0CjB6meR8Uk8ozmTmRbueKsjD0RsNc1NYgnES/zSGOTTBGI9H74uTOKlxktFbLBO4rYvn0R72fDbVbwFFX28QluZQVZObwhhMkyh6KcxhvKY4hznHQ88b+eQJzLmbQsDm+nxzh5R438emY0cNAPyXzRRGWrZTOKW8g0md4ZQncVMzbZsOXMkYNHqPvpfjHLpcTkcjTy7w6Bx5UtaZlvS6rZgpJLoyycaKdy3ZjGxLVpEeGdB5j1JVwnqSWotIs4Ciq4mZCxz6emJmyeTLmYwfzkqZQm1jDEt644JyLOnQPRWOJXYENx8jDB+ZYwik17CrPlJeNEp6KoIRM3EKr1RrZWI4K5PVpli8oYkdYddXsD4pK5OeJRBvZeJGVibwxHAI9Nb6dexPGSDWsaCQrFzkyaCUs4maG4qAvO0UvysG3N4hoS2x7fOcmGSS/RXvgOJHDihqnteidrye5yWBO1GEOeh0/20lHr37sfIcjyWZxF1yZj2H8PETf9mf3fz1p8v9aX1WO3s0yyUnE02OnJvi3+Tm0PhcOIKlkItzaD+8kFkeIdj19KE7sPFPXy/PticN45cy32SIhrqXOUj3PAK6r+O7qL1QLOeFso4D48MkHpyPR2CueN1twiLXCc9dJ2z/PuHLHCejYOZwdRR+KG7kh7KOSBbcJP5X8JPIOiFMcESqgfe7nih6qllIM6lmIc9hGIUyhxWmcEPxIzeUdeZEUZQrOHJ9iW4SUyz6SaZOkbXAAlQVbYzHMz6KaabRUMxzbJR4w436d01mkl9RspNshpJ2PiAXkOQmuSolP8kwKim3h2eXu+pCXf015USB+jaRMxuRfEnk+al/ZUxljlkUXYxusTXsJAcivh7dILRkfoI0AvlJLk2ZstVtZaPz1W1FSwq3o1ocejNo5EMz1uoURVvo3aDBZyr0s2krI/rdtJURrfPXSIh9LmrlozNvE/k7obpfXZ8f9qePhxn0C8X4XpTEGZb7jHYjlO9axknDUZ+R8ER3txHDXyC++z9dnn/6ertdY5T3Gy7mG1LeT88P3+n8ev8ry3U/2V5+Pt/8tv1860DMUN4vt/tTHd0dBtUAFyKhiceAxYACtwOkqjScEv+cur6v1jIUuvF1IXI+TvW6lejvgkiy8t/F9WnvINEauT2bFytQ4/TuFiHvju9mJfW9q8VgFVi2WcZrjYx4NFsjI60WxGuJt6vrfR2cbqMSkiYD8jsEkpMB8R0EtgHINlsmKUqImSQpIbPEH2SW+IM4llYGCMuJn6PPoLBWW01s7+sqKMzVGhnlWdIHSirL7fPNU1H3aeZONFmrZ+wsS//Oc1zW6l/CuTmiBwqbtQZzPEwSPeBN1exxXNUa7cyzpBTYow9SlPBal804ldbe7UHeYc2OLNYwqm7sPdMqjNbmcdv9Wm57n22fZskIKJlct6TupfrH/uacd15dtRdoA27V9gOeO6T8UQ9Ck9gdRWJfJaUQJkk7BD9JkkHhm2ZHxmkT7a8VNmqNduZZWgxKg02ktdHMEQ7gvdXqbY2sqbL01mzeY20aqb1BsqddNstR/NXKHH+1ViuV20B5Dsg8v9x+3m2ut2e/DBzW5nl6awnuhSK4r9FXSJPkHhJ5OCpdvv0k0Yc0SfRBwXBPkxjuecBwp84saeCxhhxZqjFwL7VulDmOynmOWoOCtR7nuKY1SP1hjsyCgqgeB0R1alUOy1X5p9Ov+0+PNageY51aPxGpBeUpCKghLQAGY8qff8p4yIUtdoo8RJljOV7IJT/0lnzeXa2hJxDmaFaUeAStiUKXxhDHvDxHeKKUOUIYS4o7V5x5KRv0fiXxvddahvjeb62boxjBOK11JSzU3PYNwrrlye2t5qY5yhZLLjun3I60VntVRqjWil3CQsTwOS7MDAO++6UtuU90r+CMNVtXsMKGOQIadpLag51jvLykqzOr77PF9xmkqEtlB24MZsRjZ64wjWCWnkqY9ohMcV9e8tnXSFvQZmtmrdlaV9mCLxfLiM9O7VfIAi75CHoWPON9lvua+zaRyy4cl32NaoWb4j++ZLEjO0l/I3FzjJgVFmxDB7Z5yh5ujgW5wowtjLzYmN3p2ZvBc9xql70+yy99SWVnplhAyNNuipwHLZXuRyR2ZIqFPpM7zrFZ9mmK9br1kyRP/CTJE65anEbsdOYglUYMdWaYIxy34OgzWkRuO7TkeRoR2NcIpBCC570BHyZZM4c8xy+dlzvPq4npPYGSqLOFKYicJc9Vx32YaVH0vJan3hsdMUxRi4lxiud6TJPUYmKeZJAeyyRlmGQmKcMk3bh/jodCRO+SrJOgGfHS59mlL2rFGmvzzuU/0abkja8f55ibp8Tr2VDc83XqNqlMUrfJZpIoTbaTZHIyOxsAPN6ShI4FRfQ7s2I2CHLMymGOxE2mJ4OsZaf3lWlynqSVk8sk8ZhiJsnZFOVuEV+yDg93wFsI7R0rdMBgR/QL/Ii9TukX+Dkc9tbnCHNEaEqcJEJT0iQRmsI+jiKofcvXkRtmlMbMUZ8RQz4IQbanRndLQLpQjJuivCLGT1GCEcM9CvWJD2LiHBkXMWmOsIwYeiIU5CuWOeIwYs0cuRqxyg2hvNoQ2luBWOXEQLrUsmcmQDRVrJ8iUCO033brc0dutnWhtMK7brdIuXmO/o7YMkc2R3iH7UbThPLNa2WjsxBuRXNTFHiEJhm38qGrZXUKuyiqxq4eiAZMtDKiqwKtjFjhtUZCjiXRNPJZ1IUp9SW/XtTnVjhJreOzO/t8syF9PWtoATVkl2RRraZkl/zvT8Wo1nqnknDy30dzaii79C83NE1Qccm+qeLSxX53eXIT4PZx5eoQ5/P2v+/PnusvzZNE6pEUZA4xxc3h2/g5dBuluEt8Xt1pfqOO8FFv97c9Qo6Oj1PnnUwhwZQpHBji5vE4LsaCM5YNmoCgMocdo9A6imuljnrsIR6m2miX1rcYgbCk+U6oNh+NnkM7MdV7VMwUFo3QsyGOZwOBW/XVfvN9EaRVRBPxc4gmMonqo5A98gPVI2rfctp9S7JyTgPIQSlHs6F1hlwugGqam8MKcnNIQQTc1Ve7za9TTvLVfvMrVZR6ZBE3iRbk0hwSlMtzWFlOqw7zrS+WBBw06yHmcHv8HGqPp8c5oEfo6XFugaBhCrvGzyH78HhWO5BHWkNh8jptPKvdtoJOFANAGAU7g65DKyWZlTpJvubb69dpJvlan/mV8kkdikeIUzgxPPBV1moldShOQTcznqk2/3To/u3ll/4DhJnPUor2aKyqSMKhAOASDYx1K3WVOmMwsrg/YFOjMbLA2Y3XVwoDdaU1FKFYptCgeKumaqOScvQDXCBaMSkM9JJW0HUSuyEA9wwa9go8HSSdH0Z6KzuMlFSVszSjcNYv/rQSnsNs4jG8cSAHxdya4kAUanxpigM9KKIulr6LBc1fVipPdb7lAgoMNXvaEO6Vx+AuYBehV4+i/Xpj+fDuP15+Ob9890/3yb7759tkx6VHedPS4+fL3dn2k7LSiJ1hF9q0i2Ps0w93apDYZH/8gdgvRfamu+kKNU9SVsbFNV1dhzd2i43YwcTioROrGA+cdvB9RXpdWegwfmU9svFJ/BzHFTvLccWOHFfISWvBSbuoT6KDG9GLCMrRjcSO7PCG6E/0wLQDZxY0Tv3DF4V+8rhyZxRqyuOodo4assgkNWSh9o4yqE1ym9laZxbphYn04DIrbVlcVYfYr7NocXXhZN8vP66CEjkzB9nktKeoBK7HTmgF3XFR1LHjJq20bJFeFwYe2DSuX0Ye5zQOmuZgbhRFyJFzCzf40BO8N+xhgLNvAYMC2B1ah8fi+TpdaCRregaFlU4v0hlOPvL4p3GBNfFoqHHQPAfh48schE9gZc6BCxZRs5ROE4mipfRa6HgczcyypYPhMBrbl7WFyx5SJCTdIm3ANTpk5cl05AEDHJXrZTJDn0pXOr1ILxvexwKosDkeJjMOOkfANs7Rr42R3KUBGEYkH6IAmZeYVZszkiz7NGVXli2l/TESj18Zl954NMs4ppsCr+BtXmTg8kKtvA5ceRM7RYDHrJRU4xmJTHoEA+DaxE4RNygKYlFkikcMcD6hDWOAVZ53jwlzzGP8oJxGHR5GBjLjs0MYWMdgQ6EehX1zrXdNoeEcw49fDA3uGMe0U7RUi0yp8BdlgQJ9DiueW8WA17DC3p7TwBcGi1LvvkSLlo4HSKZRA+OYZY4OqTFzdEiN8hnWorespS0Mttkjsp/GkUEzEtSrjhBQaF0Zr8z1j5HegGJ3kDzyjkEVUMfVZFNokVegRG0mqaPyxe6GHKUlKxaQmqdlSxatVrIvrq1GhpWam36lZ4yr9p1f6yDTVfLklX4axWxbdMfORjgxxMGzFYN9bmr0kbDvra18HK+rCZDhaUkrQaIGFSrWhreC8Sosahq4IyEBwTKt5SfntzprRJvZa1GryUUDm53R5AXIEW41rZlkPY0Y/eNdXgBI1L0tSPTr2eb66+Xl7vqYONEHCObzK93ypztIUfD0/lC4CaR+uWN9Nl9KfsYurBQNClSdX2BMx6LNj3C8hIiYR9bUHLhIJ6UnItAbmab+IpdYujIUJ2FP6/q9lh5HiNOTKKevw6evdVqkdxhI5CBQ7/BtBuCU6dJIj0bgHY84pS9Xv1FUHoXnJ+FPR+I4o0HucaC2AnFqZQA5VUL7BwI56MwGCjri1VsEouwvgd0igIqR8FMHARzzU8cdBcBaH1NFwU3oQ1aBT+0XmNrhacC92HquLk73141h/yH08O4v4arEoMQy5aeSAZSHvHKTNPgm6ej5BLyLOX4+mak4V4+TDZwGEN5HveJUiD64lbt1ZPLS4emDXaEAr8AhKQ9UerhjWz2a1524kLaGI6hNenrqlGOgXfNKAR/X4Ycs4K3gYQu4NQVWk2STKIEeHdDc96Gver3Y/qIenG7QI13i54tnhnAETUna2hLhb4Q0RUQzzBHNXaBi8aNXAA7ZLwV+kIOXH6BlyXMXlKawcx4gP0Q3QRazPz1pP0wEYE7LACE0iRinCEzGNEXCdoGqJR9kyaNR1Ewsh1wUEr1NIUR6+iSHADPVVyasI9x8VdWkPOshvRGOoGZKO20ieH3adrNO0El5iuRmKuTQBK7F2awXgOwv0KCIT088NatlRgYSO8zIRjrTz9d9pNVwEHJLjlOkZHOaoZaZM3mFQRqo2HSQF9PCbjnAcljsOhqO70N9uQ0H6gTWiQ0A6Pr5uoslzFdMLXGGyiQNAa7OvpcWnsDFoq7Po5grHnLRNuxsQSpzRnsNAnOW9bqvg1KqIecPwCS25ghqkEss8DTlUgYF3FG/ZFDAHdXYJQqYugZF8ha0RAZjcwFxM7EMKjKOMMHU2SqOgMHM2Qpqq1srC7gOKwwLnvLA4UZfxhlisxZ36HmkXwBNzOtYDX4AGcamSEKwMboLTibWcyE9pgFj2iXGmJlIUI+wEynPRR17lNVkJdBsipVIYNdmfy1xwPA5qQDH6SVqFz3INSgLUpRnIyhPxx7nEFKQU/pSWwa7xlpiQZwL2ukHIRA5z9NDqnECzW6qhom649FzaCFyPnKK2XU7CMZHepeVswKNz+5bkOSlZ2ebtUhUqzNggTImvSEgkU7aZwhiYdFGQxBJxgeeelONE3nmWTVOYocQcgH3WbdiB2LF9lr2WP2WFyj2WCOGcuYgXRroDQq5adM2RBCGmVb0aqCNQ+DZb9U4UbFxROS8E9ibE4TWDurdCEua3owiAodnVSKhrojkxell0OqGEem5BKVKzyXk+YE2LWowISI9l+pPDjHqWJ75zViekSc+1293CzwEY1Pzlk1/MdNRc8N6+xdgCsIyZEbzn1MW4eYvgBoMRzW/nbeLTfTShLyS0HZSNyNgTHj9evbuX+6+ypDy6t+U8vplf7q/3l7+bXN1st+dnew2F9uTX3TsV2IgbcSsH0nN1F8NKakPKV75svSNVzhuxLMqSKsxHTItYoWb+/YsnDVvNVhUkDsJQ5bxAU8YWi/vNgG8N/N8i7ySJ7vpcggVRNlAEWVBfg4AuLYs+A7K1M/hYtKlqBYBmvf4blzXEh/ID1xXVpCe4TWLrk1BNGOcKssEtcdgdcochiOtedP4/ES9adOliEngA8nAqQXZ1uzApYXb1+rR8hwWnYLdKmsNWXqcJ2f5T2YGTFZqDSmqJcSxG0UByKF+CouM92dpdCi/TdRtZ/hdIg9cWMBVG5BIpMs9CBXMmwlENvwEzm4RiWKrYjEz5cuyhtvl/RzKJr9VhJUmLJse8w0v7+D0QZ6WGla6rnS5ffyOUXdI4XcM/20i3VRUszQ45kRRT9jrDhT1YGEKGybM4cEFxU1iJX904zBbFfSGABAo2QuCAH4a9P0AyFOmEEo0pisrbVbcHJsVB9msqClW8JoR03yOWMzcgDGU88osCkkyM0gVtA1L3cxF2JFT5w06NowMvFbG+4gb2Kww20g9VpwB3KeZmbKSmLnpYKwJk5RNh9Og5GIG1UKRyV0AKL3TXirh2zr3lF5f0ltA+raOfbnpYPlzXA9Cx79smg+jz+xFGhH8JPcAIGQhj0uJYmeuAI8XmUEJKfR+UH3tKfTcqF6HS5hOe6DplWUKvTJ/W+epsumhMAu9OTQg3UZJ1dddPpc8y/GpoZWyqM4NrWhuDizWTMLYG3pzaNTEDP0g1AAOGxrw6xDGLXn+t4IQVtkbgOP4kKswpdbOAXJb+h7QKofTK3+jamr9BPA2vojYcAwEuo1HwI1a8jUVyzTPAXjS2swtYwf6QbUBFxT6RbUBLBBhtpk0MlKhtplGNK/A081gL7Z6KOqQqeU7QDN/WGXoIS0bE/pptwFHkqzCiE7ouYv97vLkZmm9xX1dHVbYz9v/flhiOTsXetZnANn5p/vV+92f71bvMb4z/P8K37lZSFh+ZwMfITO10zLVYdAdma9My3fG/PJc9mZa8jjst5F5UCGW09tB38ktLL7xuE4s6zk8SYsiVqNZIYkZEXBwITmKS4XY4Y2NF4gFkL3DDd528AGWlzmzryRxe8xu5jsJ8Z0srPxsYH1Zq3NuRXR8LcOxNEw/sDPN1SZan0vMTDbH5E7yr2JnIL8EAoOx6v47tNA68LQionNf3ujg1ridjoUVbV+a6YB97L8BzjnjkRUJPV+JrKg/56ADUup7EAvRbGEW8SQq2iUdic4Z7AgR2PIrJBSXfUHvfTTn+KUQShLezfDhuoAkYwtOeMSeUG6R8FSLvY7ViNdAXki0HfPIT4c5dJmBpw43QaEGe8tPUCww8RQvndY7ep5j6eGPjqaTXaAnOJZdnIGK8sprFXqr8qwoIXDuof11ZIBjpvZeGYCZqaOY7iQWhDuJAQex4FSHfuLMT8Oee90duM1NBphnaJsRyj6HOoI6YENfYKKpdwTgGSGs1MH1AEoa3pIgYxzLXdXrbiuiuptzFjvErSYMYNNEKD+ATjNv7m/w5B4V8lGIENMCiM285b/ZkyetfyOz8p3xlB91B9zqW0xS3BGREcEK1phZHby63LCAs6OHXqhDnKaM8YZVjAWQHj1eQ/0QNNWRtyuORM02EZHK9Pmnr6fbS0WFOjIV6p9Ov+4/LUbD5ddPu835/nRYl37xh6f7zz9fv/hDQf7wYnfz1XaHY+nn7dXijx30q18/Hs6xhzGy+EOPl8AvDufnzfX55m7s1ha00FMKEiKQxySHxoGkF8gTgboiSIEIZHqBIhHI9gKxR4jHT6cSOeuNM3RtoA8RfmLGzyYymnFhM5aJGf+2vTn8M5Agw6brJqb7at2C07aKSQo5BLDdYWbOkOX6D3cFjZmxE1P+ebf99W90ypolNg7kqPClERJSVcQNA30qfF9CxJifVsarw3fefj6E3p79UntJedLief6Ycvv1z+7+x6ub/9re/F+fL3e7s+WWf/jcNwjGw5fs16xHaZhFGvgP58WP3vyDmEYmQj+J3pQcFRmVlxnFF/9gbTPH58tWb/+qCydpTktIKdcN85KuVJVXDPKReha+NAB6AosKODo6rGZ03Ih4Pv+H1syRpDgeAlYEi0I6vnQBbgzCK+gvRyDeg6+nU2h04aL4Pmyqba2C/dp7YzrY7iB2xDS1jU8g/XI7HheYHM4P22u6OmwB/ns7UPkCh1bupkPMrIJPAEdMrEQMtjLqvNjrO0/MgYAPNT+cAr73CbzwWVlKvmuQlR3IdqGbQLeRxF7l8HHm46CR3bnkicFv8VHq8yCpfk8VOCliOQwGzMkOqt5oTsAIDSy8qjuNAr7gEwMs4Ke1gI+QMNoEustYwC9ciRghaZBTdx8Jmc4JGSEFzKkuIcXecvo7d8QnAbFTRhk0sX98iviot8QSFkenHNudixFf7a3Dh2iMaFb1vkp8VpZS6AKHWez2HbH+B3ycpdEGYLtzKRE7AHGgS6PRL90pmYjRX/BxljyalR0IdsFZAeMssSf//jtEwmcDc/VNw8NPd44mfPQLcUvNw+NPd05mfPQLcXHIgmZlB2JdcFbAOMvsyV+6J4OMzwZJ+DjLo73AdedoJkZ/IcbZaPS77pzMBf3zutoRfr91xN2hsC71rns4KMzDbH5UPB+LhdHFnbQI/jb1T89DAwAj4xIUiANDKYTBT+ojkbDR274ZyIPh78FmIBB2+Haby91P+7Pd5d86j4QvDdkXuOjtp1+3Zye7TzfDYHNxeX6yu7ran31+PxAUG/xwmPu7Fvzdyc0V8GfT3J91aGvd3N/16O+Wub8bwN+VyaMqgr/rJg+rxK90DfMsukYcVpSITbNGbOgicaAqsibDFdilohtZJg6r6oA33fPiX0puJjmuE7uuJILwVVQEFWEVIDtEDYAChFhcwMAGbc0yUh/7NSSgORcYZMlDMqTaXKsG1x8yxPHDMp+28DXLMJKaG0bKeMeJVRYfh/Jzje9Quo0cVrhS98+JiRTxj8igMkI3P+a0TXzDpCzsxZHKW+MjdKeS4EPe4N/AmeEE7yXl6HJXIw6+tRCrl3O6sln9+w3xDN3PN0QzdEe4w0c4MQEJSEN3fSAwDNYQn68oq1L17+dHQ727hHpipBMbmR8Wtbrzxju6UNMIhC/xNuKf0AdlxafxCYdlre4sIlAMNhPfcLSw2+7sYWAM3SEa8KOMENtgsMpqSgNbPXzK7454ArwgHv+GYVjI6k6fEOgCQyMQvtALcRwNSVmpaHzDYemqO+KHYAXpjvPIP943Ammf6xvm06OR7bpjKDr+tR8R945e+drv35SLEwP/hO6BqRAj/4pRl9+PCX1Pqaucx+GFQGz37wv/vtFwOzd8lQAZegQW4hHMi3zDhF6H6x1HgCE2xERLxDWZGLAp0LdbKNsIXkcbfUiUhYm2ZvYeCDW1YDe3ekszPjky3lICKGGJGTcESnQnBoGTsMQIzvzlAmprAG8DjcYSRy1ibWLwEsR0HeIl+ntFJtBCEW8sgaNgjizFgufLemMJ3IQjFqfiwPNdIyuvoNkB5QgKwyBE3EjW+VYxQVOTCprIMh/Ju4x4Aa1kZZWPS8m+qvIdeuf5v7h2KbIUepiZOvPV0OOqTr40FuSBNvIYHvbE9H7d0WdbA/AhDV9cBJBZYojJfP8ahPBUDV8fBFBOwoAKLGyRLCaDVcfGFy/gn9fHmzV0o5AvO66+W9tpFFN8twYeGJYnfyKzgym9Z3i42YCVMRvfNWJ/3ej/xNZAoUGR2ajI/GGq4wH+pjIsFYZO/4llL8fI8KWkC+BxNi6Xd4SnRTz2141+CmSZFxlmRIHcwKOMkBrAF45hkbzbd4V8k0CGGCEHgG+cQy2A0BlgTqA/rvcRwfbHlzHnyaDI+HKBrE4j48uNNoHeHkDUyzM+vujiOTTAClZXro+wYam8e1ThS+XIcCDI/8SpjC+gI6PMD0mhvfMroQBg8a3SR/YRDhlnPmG170ZLM1uQh0ZKwXKqj32iim4zPM4YXYACj7NhLf3xilj9a8c+gCLjjNABEPxINtQBkN6VLkTsrxv9lFjIATQiMhsVGfuhsEAEZJzFISm0d+Qg5AAkwuMs8uQ4RI8qKrlx8lZKjRJ5ahwi3hZ5apyr5xfB97u6CFxM9EMbwNSUyBPjGs3jFczicsSgj8Lh5ROwt00NuIX86s+7L/uT7emNfvbZdfsF65nryuM43Jxcbk9+aVCKJNEYd9v70Iui/jDrqE+axQT3DHNkUfIf5Wz0KbMgSullHOGMV3QyCzfLvYwznPGawUyDjXvjIuMz8P4Qqck508jM3vzL+Py7P5Cocmbnn8Rezh6EYNUtSDOvC+oRrcxIV84AjKrkRBdxAGyoZLTa0ujDQpdLkMYWmpeFtHUILLivAdSbyugxRLyljn3DhloKvkI3Wsq+QkMNjeRLKtTOBD2GNpqZyXdLqJmFfdAD2ukM+CQX6pK29JMc0FJnhH1SglrqsOebRks9+3wDtTSwjxpQSyP7BADlmrDHjkb/sez+pEf9uCa539Hk/vSN+uEMA2qcmtufOCDSq5RecftdjM0kLY36AYwrnILyD4V1dNhYbzaPwoHyC/TbUgZm5gKBQL0tvZmlhqN8Jvx9stVAmT4SZ0RSvdBhAVUox4gC3I/PerMBawZzn1X1z1HoW+PXHUgwafw5P7kazeBBbQWYTgzt31Y/fnVMMRYK1uDZZhD/1ejDwt49kLYuwQ1fv1zUttcPoRqtviKd7C9Pvu6v/3r43316DPPT/vLq+q+kkdfd8ngr/3Vz9D0skxfby9tl8sf3/+t//M/336j93jcPGmNXhtD5Ko7GHyFDhcBoGPxTewhZ02hnIJE1UDNZUQOomQkCeDSamUGkcl0izzl+O2p4VzAGDPdrL6L86LxVVitIw5r46uzq46vT7KsTb26VNBxj/WCF6A9Hm70g+o/Oe7oEcysw+vSculgOK+EDCiauD1QflaUbbhjc1Kh+QFfiBZQErP9QPZZB9Gqjw4qubET2lzOVadPor2DYvQcaucGC8NV6RwVhoabINF1AVbA62/Oh8Xj56dYmXMC2yFbLdWW6xgYQ2MdS7ONir6WtFmaydqj7CgWDaNZzpK0scu8r0AoR1nfDia5g1wqnrKW1wnmW8OdWvP3Fpv/aAnkCMv4ctchGj7+rxail/HE5veL83fTP6/NSK0te3BVxA3WRJzyKpvWvjwmdL1Jo4I51dUM3XvUCsb10jOrFw2gBWNEuie750Pq38tl2jNLGw+IEULFdGtf9w0O06t/z4La6Ea9LvNI79rETynpt5MXj26xHBnfhCapI1SozrEtipCx1NuovXNZ/CPXv8pZvXP8X+8LVvDpn4a+4SPUyO/5KDo0Ez8dFZlQOKKu4vmYwqJ5MDNAE0lIbWWX6Fgh9XFZtFvq0BVSbbaxpBb2a1ruqsCKzUE8NVUG628cQptPdxhYwHfBEcB8uvdmJoNA1UD8vZZX1jFtAjMCU3byU1QJ6bgFuAtOWeWn/tj38p1y6hU3XzktXVR33C0wWmLKZOP00ZBFvLH8sjPVIwh+h8/iA6Sm9Fk/E9XxtBorLY4ixuJH/UlBcnk/zOu5fDgPtevflEOPj6dfdxeX+9g3wdPtxdxix76358O4/3nvTvPuv+9N3//TkTfPD+18PI/zuiJCTSLbFpJtb9f3AlVupo6eoh0z2J7fHX3Z9f5wJP5Dz/yET5A+fzcL7P3TQHy53z/s/vO3X2++1310tT/iP4k4Kt7JqpVMUUmT1kikvploP5BUiZtVAgRdFrQeKCue0aqCkO6RtbH6rU1rWndHmZKzzB1SMQwAJaI3uDDWnJ+hD1AIFSp1U56Sr13oW3Tlq0gzRHKSs051W56SsOq5SWNmHcdHQf1essYgTWFTErYupJ8V2AlRELO19Z1epYjZFHlnrOw6HYF8ic6SFjhe18R2Z0WsztBf/kFrVMQCD2zXaFs1hCWHYK2zvGhl6ELbW8BmWoFgZII0GenBYzeB4ZYsozbGQFIc6i1A+tZaAVjhc3YuWZtdq6VgEszumnKGrBpDvt7O8cx8y1pwoDffqU4KA4D5450GtH87Uro+94x3zoL6LSqO7Rt+NKhpdv3nHWEYSPV9Ypzqk57zROczVO44QTgt4y4cWY90hRwBkE9FvXmnt1ui4oRNAd61jpNGIIcfYjRG7qs9KT7VG3w3V0nKv7xi5NGKh5PXSoL4LovQyq/ddGNX++idLRiqN2GIVZmRQ30Wlh1ij7whVtUI0foTzdt3dJxSlJVi9kdHwnl4IIDpapafXbfA3eaGLAl/DqsjeyNeFNvV3Xa94RQfA1QrBtA1ARo2ad+h6Dz7NuBsQ9uXup/3Z7vJvnYP3y7rPAsz9cXt1mBM3cS6eiju1H83oj4Z5v1nA35zXzAWWtP+Tad5PWrSVbt5vCvqbZd5vOvA3Zd4IWuBL+7/pJg4hxfpRJW4n1rjnlYUo8SKRWsY9ifXtuc0C/134/S9pXXu4hF49QqWXpj22CWofGtRtuvazmXfqQUxxs+WfBYFXfgaP+uQFPA7rtE9wkfrMr5/GW6/0FJAVt7VnROsIK/IcQXhsYwwm/l2vHiiD3p71vy60tSfQ5cUoHxer5bNiWSkH5PsVwXDE9X5jROh6BtZj2bneICpB+RBZ7+dIG6ciYyGRyGjo42UM2dzoNvpts140HWrMdf2rjdW9hdZLzIYFa0M+6IZ1P4HM4w0G4m71G+tz0ooTlW+qjQ+QaINa6AtkzKC20UbQ1qTx54S1G7FzWkJ8rbuHWivKp936J7QONMhtZMPb9iIjgECP2Ex8hIjJRrYam+gH50agTPv/Qr1WQP/felY0TkN6O6sl3NzE499wqHfWvQZZAlEh3ZEvXvnU3eitQLskI0NCIuiS3GjkUCe0O84FH+euPzS1T+71znYGdGmuZ+N48jZkae2U5O2Nf1NkplO8uiNe2g4Vbm+YaTsFZMnVI0VQJLGVCc+naGXCU7axoVdoQUPoGw5d4h5u6vWOI9AOG2KiMUphxID1tEo7li16X270IavLh7WV1R7Cmpqw22WjpZn0XsRaSmu1Q00NBrvk1JvKYB+IETw2iwvdrBx9G4C6yrMmdFhjacF2LNsInsIbfZhYJzSssZk+X0KNLeD5st5YAvvgiMUpWvB818hKg+ZGqAsaNDcSlxblWsE3aGpyWV6Ti0L3v5LkatfKrF6Si6ov5lcZvRTkEpFmjrxuuqkHQhVpG3+uEKSt82kMKkdU/3NLH5IBFJRNQp+YAbtLy6hWPTqsAtkqhG2ABTHBWrD1TxPpwh3Uh7yKOtSHma2dQV1Y2OoX0gVMWR72cbYZFCyvf+1My7MC3UcoQeEDiKieC955AVN4rfddnO9yb3PSG9LbnOcb0luihJ7gr1nMETzhbbFHcEq3w0p6z73dlmM4ytvij+CUbgmbN9yV3BJ1d9xX3A6N3npe57Ycw23blrLGF9uYY/hiEwo/hAO4OY7btvnduW0bP8kW24Sj2HabeBS7bJMm2WWbvMoN3HwXt+3sXl71mjL8wtvDdV2wrZ3pT00DCnrW0dZNdHW2fqbfsg0znZBtnOlRbNMqj2LKOQ33KFY4pyHmpwucwdHtI0VamreywCccwT5SXgsONJ+CROS72EfG+Cqll6LqElIzSXcM+0gRfwz7SFHoVcR6s+Mx7COFUpmA7SNF8u/MPlIY07qefaS4SYaMQsE7YMNIoXzgYHtLodAXHf82AeAWttdrYY2NpQAQC9P79XQM+0ihhCZg+0hxZY3PozDGb7j7pHh7BPtIGUpL9MwTxbtJbnni/SrfPvHhKG57orBZU7ntvXKKlvDqhBJe/TepeWZhrNh49zrx+Sime+LLUUwCJZjv4pYnrywSJTTlnQCbtp5bmSzQIPPd9iS472GXdzPKW93j19j9SQjf0QpLQvNxIcSjWmEd2onfERbglWNaYd1cpX541T+vF7NWlvkoVlhCC4PorLBqa0Dzi0QzyQpLoj2KFZZEOYoVlkT3e7PCEkp2BDc4kjjLykpiXGWqJQrQC/ax8yorLNE4xiFPaIx/HGGFJQxYhrDCkiVa5v9gK6zOW31yR7HCEg2yBxoJ6yyrhML24FZawoB7MjFA8xorLEnl9+aGJNn83tyQJNvfoxuSZPk9uSFJdr83NyRZALZ+J25IwkipdN2QhDEHJNyQJKejuCFJzkdxLZJcjuKyJMVMcC2qxbUTXJbGbkh/uNEAo62Q8ltYIb1aYrt2SLftfnqjuNjtPm2+nH/6errbuKEl0vM/vrg8/N2hT389fKBajKnuSGmWO1Kc5Y4UZrkj+VnuSG6WO5JMdUeKb+01lLXaq2/m51Te0oFKTz43iumDmDrZt7S34k2d5C1NnXRWQ+4tbbN0hk4aVeD17kiu62IUVY5NADnnMe7H3fbQ3e0Hq83txf1mo759dXmxwdcRbwvEWjt81IcvQHinDr9AsLXDG314Ow6f9NFlHN3rozuga/TR/TC66IOHYfCgDx6HwbM++Hiy2hWdPp6rdsWAGU9Vqx/sbjxTRT9R3Xiiin6VceOJKvol0o0nqujXdzeeqE4/U914pjr9VHVRcZMC6sMOV7sO640BXUvr2uH+hKJxWnMvNa1dqxDv6Hq5zqrQveTp+1ey29LKcSxgJH1vJKt4KADK5l6jUYG4N/UdC/s+TfipGPEw0pyRAUKZjypPxfEX4bEvTjWaX4L0fGum+6yEFpKOg6+nk/OtlBj4HWFgNlRTejJTrHv6WNgKse5UJOusFINTWCkCpLzg11s0xnX+UU8WjXIEA6lN3/WNUFPazHSQ2mTQQUpj8chguhI+VoaqSZuuSRyD4yJsACOvOo8MtOh16NNW48fsh+4XJfYkwuUtrnLZjAqXTWSYFTCpakdTgCp8lBF4KmI1Szpqbavp+EwgxkgaoaC7++YQfdRdyAjsUcAHWEpgTjKwAlIarMaxew9ikQqMr2xUbOtGy4eqQba7XBCyQcROmx1q2yqAvw7r+kqAPyyxBOb1ZrJxnb+OJS5MOeto+K1PMvRn6K4ZjEoQcYEoFrW4rTZqrAfUPRAygkDEiY5RBCKOZLwkEDTOStQJNLQ+yVC7vjv1GTUgwvB5LAfUnTxj/53+Y49lpH+IeybjxSPEPZNx4xHi+L/046H0O5rfZXQQktz/LoQgFuGR/dKih/U6HnvxONPvl8JbOCPjjZbUcd1zAuPP8/QKjeiP8tCM8tY2zGNToKenarPWFugR+lQQVE5Q4PcMMpwi/0xf1poEPT0vm5FN0MiMeWzfu/306/bsZPfppcvsojpWR1fWRbqWZkOD5NJ3z00M3HHfPzfUkjl8/9Rg5+Y36DbU4dm+wXBDnaDl+0/TBahlkFv5/rlFfjXPI58qsLS+ws7aSqvgtjS8AmvrlH/0oZ1w4Zr3zNKZbL8qR9rX9seuKdTvDMgnnmHFFWD/aesU5XXEvY5Sa3KwsfXSK4ss9VLO1v7lp3W2aQfB2G8x3pBjpai+ZSUlFcV8Wl6xppVh0RZB62DjMYalb4nKYFgYn9exNFTXXZDRhiJMQhk8S9c80QdMX6b155E2KYM6PSkLn3Gt59aGsMv1ZY1H+EtzLaRKGNb6bBFGrUFZF4yI6xZp6E0gRIjFMIQ1NuGBrgsGxEuLNMsOWVdda3ylwprTISMpmjVW2YSkj8WN6hn/rP7GEx3mstD6c6V+dOMLEio6lliQCeyHJQznh9iP/imJAH/0TbMJZR0hzg5Ds6m+A3eyylpTfWgkWeW8TSA7hNhECcmZvml2CrQnIfQNI2aN0coqKUs4jW+YV1llJ77QUQ+Uh+bU3bGUrbIwEteaQT2+HyDGkbx4Sfl78LrOvEIb5FiZeYW2hkN0RhXaGuaXlJpI1zU753X+3QqdEGjoFYNTW0VBPbU4tVUTXnBqqya8o+XAoAFePE6Z1aSNGhw2xlqJq/3PR45UIyqvptW00yH2rQrMEBYFd9qAzyJ1BTcCwLLBnceX5lUj3rKmzY60VES+09JnasCG1qSM2SC2vlJcaTofBw5TA4a2psGZtG7EvlFBed+KlK3B3jf8yEYKfJFAvpEVmIuuabFjPSKhr2Q9THHXJM0/HEA9HcHXET/ynBoR7zVtzqwZJfahCsznVyQt6KOCHzlKoU8dyHcGBDvE6dvsWNdL6EMhSh1RnzT/IAH1NPog0fj+CZZU0LQZn1GOOEQA+htOf4pw6GOGH1kxjYQeNMlpRPwQpyen4OYjcT2JclkhHyGuVdhfOj1hIBfOsMN5GD6ydI3iMC6Wg9288sVxL00xXGw65TCAg/thVncccRlUamj8Oe+0ZhH/qY5s67e1PlNStb+OgNEU8lyEuBp73l8NsWBmUAY21rIVwKWqDY5pmFyh2Bo78oRCoTXQl810WMtbQXWU878NHJ9QvA8y3oLl9fgRfyqhH7iQ8TbEHmxKZ7iNHZhK7wMEFv8DGXlF2rcA6afEgoqgsUK//0FDpYAWC6sNhzw8zsbaFK4zziiLIXikDNEJm97yQXn9wOOMcfqx8Dgbu/7YXksT+fhpecOf8QMjNMoKiYVChskQqdAbuclCf1zv+CRkc5ARRmAV8CVnKELRO3UQ6IQAD3lCmiLD44vwxKnvwVFlidPbglOB/tiOrGnA9iAjbKxD0VttMg0+Q4ZDpt+dkWFPoAksvlcyohURHmVDXEH3ZpBBNFp9kBJOJDbi46ywT9TINy3DN+Te6YnwGrH4ObEI+zCNjLNCo9aQcVY8BqWrj5QhDkB6aweBAhD8TDYWouht4owOhcdHaWGhgcA4c8awz/LAOHPGYsA8UxeAJsY+fEt0xmE52XpOns3JIv0UWAQi9E2HNZHSa2liixYW6f1MgxIFCVt0mER5K0MKR9mf3I1IVw/EGyMBIsXOCv3UC4h/OOvop95Gsz34kt2Qfw80US8vRwxYfnhVEfG2VWxwC1DA0LP9/kzpnnm2P4zDzcnl9uSXFfRuZ2mmme12dsZblo/bMJqfGTvtWoAOhu2So7ZLWOi46TVL4GYddxguAA5Yq7ofy8Otur/xHq1Z7NrTnVyCLxv3N6yjtYtdNR6X1Go0fNGQI08udtG4P803zDvwRUOOuxg6mm/SWzUcy4x1vdnqHMiFCPU/550NAE94xwgj3A8CgO3lGNuS+zkMkLScS2BJuNGHfO0Wamyhy5lIY8fIhtJpKwNsyHBTGVyDx1sKFq8aLaWLV1BLA1nTgRoaySIIlGmCyjKNzstkaQFqZmGf4pF2BoxeXm8ngWSw+MAlgAz1Va7eUIe9Tzda6tn3VailgX2NhFoasRfSRksT+8IHtRSk3jZyKuxrGtJPCxwDBrZMK7y6om3d5xfABwxsmSjxrmhgHKOLogRbJg7/GV+lZF++f7QtxRi7kPsRCtgBu8gfBaGwPO031pvN28BC+eEusEnhs4ebwGqi4x6wiugJt4DVRIcdYDXBYQNYTXDY/1UTHCU8amKj7q+a2Kj5qyY27P2qCQ5bv2qCw86viuAZNn7VBId9XzXBYdtXTXDY9VUTHDZ91QSHPV81wSNdUMzAGXKBeqEKijMc7n/ebX/9G11QzJk+PuV6IJ5sAgjAu2LoA1M9v2Lp/DJwkyl8wbPU83OgeEqjeWhhs/HrAfzzxq9HmjNTgOlUeHpJQb5ZBnkgjb4qNGEDaKwnsDT3j4lAW/0SS/P1y0VNfONDqEarr0gn+8uTr/vrvx7+d58ew/y0v7y6/usQ8fD14+HqeFOyuFvtbpfHW4ebm6YflsmL7eXtMvnj+//1P/7n+2+UTrFt+zkLxm4o9b927GMs9K3p51ToWwcSdw+lClIBGt2XyMdYqJ0ZAo83MlKQIhtG3wZkZ9bdPrzlaZGIAY23ogSpkLbUNrxCqbx8yfH+1UtOaDpFL0A+KJLl1jnoqcj5uMAoa5uesbF5gD8DiEbP2NhYT3zqqETccJ/6Bn70A7re2oSyLBvTgob9TB8FSrgP2anGVuZPo1OHQiUPFL16nwqtoQUNawLy80Bpe/apHq8sq1EJXlBuXqODeHIeMj9pzI7pbTiCsvIabUxktRUbA5mEfR1vCBSMOlfvHWdUqLHGp3IW47c1chEd1KuVjNMhrFrhvA7Y9L/L+77fvJLkun9FmJfYwHBw+3e3kZcdxwESwInhfQqChcHRcGeI5VCCKM3aCfi/m6JI6g5Z1X3O+a5WG+dpFwN9xbrdXV3VVadOeeKKhifyxFUNUOSJYwcNhaSXBXPyBg3lxA4auteD+MsNLrnlpA4a4nT6WJn89sX6vAwwHS0zD5V/vnfZlMvi356eAenEmMvfEYEKJdiXaOaR9iEiq5bphGsAGLzyDkJEZVzDEbTSzzIaWMo1M3NeHruBAGKvzJCxPB74Ymu45h1qj3qZvx90Uhna7ALzENnfxVCtPEoCAJ6Z4Vp57EcsyPcy3MzESdnDl+ykYKgPWcEX+/I104L/mU0KetCWzFDJPD70C7KylX/oQycBBeU6FlkERiXIInlYLnRCK4jLdT638u9dZHdrBB+Z9vVDQZXws1EzqJWzViwIF1uqij3GnJXC4LbeF3U2JnjwZu2rxQSV7kStx6kszQzPO0AUqHI5TmV5oEfeIa1AtfNxav/5/O6fcurS0+TTcepKkILc6Ekv8UDzU9qqc8t8YFhtSQUGUAYet5IBYNkjglIS32AIpSS+wxhKSfxAQZSKdABgdpZ16QGFUUrSI4ijlIQnEEgpCc8gklISXvgXcwfegwy67NGRQ3IbDYbC5Ha+Jg7JHXyFG5FLgcIiITeolXMbazCiWp515CWxMOmIy1p1ypFWtALKnbQ/fLLcjzZ+9eHi7bvL++rR1fkPF3ee/puwfffq+3vbv/jx1e9+/PX8+vXd//mfl1ev/undm9cXNzefika/3oUInx5JvcXYw9jaPUX8J8//idH7s/g7FS5f32cQrKDiXhuvrnW7Cye+Xf747jc37+/W4Ifzd7tfxo/ffL+Clxc3+7zF00ym1ZvukZ7eJpHC7aM8yVnj8Z6k/vHum87evzu/vnn75t37s7t9eu8ONgoA033mIsZHyRIPz7O9fXNOhYoFqus528J2pr5YO+up3fAa/IPU8SKl91SO/e1pffG3OnoqzbrmAH9t3i87tNXVm+ufzn4+v9unH88ur28u3n18VLnTxwFE2Q6JdX+yfaHBXkFLZtSsBaDoCpohAnDrgLOdJHt18bMUUFPa7DHD2K/ts8TbTJVtJqBG0+2Rf+Cvmz3jDD3Yj9bSkTlUrNCGjOLhzvTjbL6OjFH5rei7C/fi3aeLdyH8ecfkt08+5Prth/ffmDMGyFvK/ks2pbwmuiM82NKFgqx9kyQ3hKGXvaqqfFUtkUVlYsHLiUhlYsAJjQPtLuEI/tqhchE9bkXoK0iPWxGmA+msFaQtWpJcAckNOxz29mI4eGd3B+0Dk+wD1zQ7dTIVLKNG4OACWXeVgKKbahsQkEP1V9nzV28+vHccVi7qH0u0c9wBXygPloA1a+R9ghS3u2T1CGBksIu+qWteNtZNJtlNFgz0aht5idiPTRsvib3MNvkuW88wehpzaf26oE9o89efLejmww8378/vf/hSxvbd4+PpO/PyL408FZt+KEAnZa8WGHjZ2RbQ6Zjvywr6HPsvR807AHj7mkhvBuT1qupwNt7fVNXfBPrqq5q7AVJulfQ2QKtIlZwNkFqtoq8R9rZhLx3T3lrQBwI18Jljuo6WsB+b91PLrN/RY+hWtEsFiJ5a5c4zECA36eEPIMmbZCgAsL8N0ifqgVLHDMU8ch0zFPOs98geVz3t2UG7MuOTDsZYZiTZVRcjPJ96Ff9Wod1Zb6TnB7CjvZMygWfTrujPuF3gYTw2xfSB99gIimAgcTSitBYAwHYk8rbSs58Dm9Vn2uLAijzmJTAqdgmYidPlDKaHH5uJtR1BDOVqgcTnGJyrBfKeYZPsAshOhg1zNtUu2GIv+mb/OEn1IKTGuanPnc77h7AV6TugMvCOmgUD2obf0BScApt4hmqx0RMLWE/47tU/fRKzRO+k7S+G3nmGJ775cPXHDx8xTOev7/6zBOBB0+svmj12V/RzNRaIHz//bMOWEztE+Cw0BtUzpStwdFp5jm32Y3y6SbQXPzKL39Ccnh2v7jq7z1//CSgiIC1Ug+01OrN7ccIGq7fZ2lUAcYOW++0VJKaObdNvpfGVYSpuZQB5+lGFnfV+Zvd0B/h1/9Aodob0wYdGe9G0g+399p5b+9Eunpe4ANVw0tJpIJuAL28kDSQ6awtAbdY7F1Yb5+NhyDAuE5+RaTSlvX1khJYIFUncQCVEk/m1Qogm2yfbcWjcy3dvrj866vcXDCY3Dv6mDMgk1JPQrikcCcxdxiAp0rd8WKB+4HBkowA/aBixHQ/5ebrI7U9Xk3mfdF06scSCFBw1OxvcjhNiW2LwViYA/xsYGm0AIAgx+LGABs1fTf12jgFCQ+yBMOV8NpRfzq+uzq7ObYaO2XexRLCQYhV7kNlr1cTI2pbW2TPptCxhpIo2+q5sp0AzC+8dlumBb+awGhD9BmQcCwxC2GBEaJFRbxW5e0s5BQS+RFnR+YZluuGbOWxnemvaJ442GiDFw/C/tAkatW70jQ6UX1AQj41Iq5FdMPvTErZptg74I6fgy1JOwOnVegr8rTZ6n81HW+30uUsUVmbuEGwwyManfJUrfQeVQSKEuIDMoAlS5cJqiTUhBKgBptXsDy+sQdmooco/OYEva/SptgFR/QTkYxtkxAC80/rGBQzAg7KHE0CLS6xFVBPBt2soDPKEkkAx2d/UXnRoHsFmUeCj3umjjgjtrFXaCz1OgAQOkKnd/nE4EE/oX8uDfRQAWcNBvgkiUMJSnwQReREMsTK/gKpg7jQp3nRg4X9cQFqwOwgwN2IQ0gS9GzY69k9HQFdmEMLn2BXgekorGAu27I4Y7Op3voUNeRKHPaFBYWGjXwFOabqfAPIKG/12Bd5WIYD3v61ToB1AUxxA2FXMgTeEo2siL72mXHohsHU+aJcKZFDOh7MBkX16A20FDfky0CZstolA24QN36OJJzrCqBK4EANgPgh8MXwHBlw9I0IUASXOomJewN7ZWEgzcnTA7n1Hh0YaDnLgI3jgHR4ZvnhsU8qkkyY0hRRYw0Ogrolu+AhSSiokui0RyBoEnCVigwcWBbFm7Ox6VeG9prRGetQgZeXCkjgiT7Ucp810tImnNvJqsPcDpYpwcGHMHJVq7oD9cYlOdthPpczGQp4cLPzxVomNf7BFwqhQPJ1QL2C/SDKY8nEQlmVjCRchkCwxryTDA4zCkmkhz1aKmEmS4HkPobB1Lm8jaNBsQDpsSgWdu/1gKHyyE3nE0BXdUJGtwDgXnE+t7D3+8kvXbSO/fwAtvfrd6/tejUXzSNCbR85vbi5++eHq8vqns1/OX/98eX1xFkkC2PMHxtqzhxk5YhMJ1ODTdgj8k4lR3769eDcBY0YHjCn1JB2isrXSKIY0aQy0B2j96UX45vruTTjX+9uTu7KcLSPZd6fNLGxw3WbQ7CoKsyu/c6P2a/RhP6/UOGOThh7w0T/Fpw8sU1emLSCb3PAAZJw0ott0ewBr7pnTmnAgba4XcJLHKamnaZeqnB2ADLYAgUNRp9Ka1tZgC+tqjwTQ2DNJQ1QcZR438R6yudmDKM18jUeRagqiFo9sVgdbziyD9tdtU0XlqD+gfec33QBfhhjXOQRdI1aEzsBgmxaQvg24PFCIFpOg3SThFminQYVti54a8FoCCOZT1u4Rm6RYjNvGoo+GC7U66xt3CVPu9gNKQ4nlxoE2Tew8HVRLDTcVYN5gczQbe44i1/i8A+ewuQ1ZDOzos5u/HJ97ruT1OniGXfBCHLcHMOvOBiTkod1cq54eSlhbtPggL7e6YMXl3pL1FujbgXnMgX4dmNkfaM5BrxukQUWM4QB2pF1WF3iYrXpyqHdZXZDlMlbVFv04jKy6oNKljKosmnIoYXnRmsMFKpm97Gv+cnzxVZ17MO/u4TjPAZZc1PSQDiaRDiRTPUCU/6ePRNs0/w+ciKa9hMqiU4iRlRdcu+A7CGGazaxQAM3exPfQqlmIEmanseU8Nn1A+xcYnEF3FyG71bWnT7oF+orQqwXIlHft6RMXPUWUmkCevKupbbpOUsjnG3C+OkllDR2vRj6nAMptnHm3mHrGed8Sc1Wn2wO4dmdNv0PNeN8CvUugMHsgpTrFJ/DNS3DyGOi7EZPci84lRk0gwz2kDPd2CB9vwLdJs5llJxMhzEZZbprRhFU7EyMsrnqaqAgk0ZXPLTMZlLDqfqIyKI40Ee+LdFRpzyWk8L2Jw0UWXVPUYIdFrxQTafAHKQQiVWN/d4hSqiasWqeIi8KRlRVZzoYU8i0FUb1qz5+0appihNlY1vAFxh/sW6iOntMSoobjQVA8MWhzVZCmKlDNjKiZtBkQi34q6K2A7L44pRfafLKqg+Cuo1TVcXZdqurkVVcWdhEVpJ1Iq+7UVcMWI8zhnY5aSFVpT0g3ahWuT+voeRlBm+pbDunW2mb7r4VrCK4/adltZKBH0rLb/DnLJCk2csx2qATmbkVOGT4kuKATekKWgryyavwiZNVV8xdzc7VVLxgjzO4Pzo157/VVIxj13uurvjDixuirJjFClr3sRctSN649jBHdEdGJeIY5i5ilZ1hfdYsRVuXsiMT13lf9YoxR2S2xpWuJJDstVdjADOrJrhspdSBCgxZU3Wv85O0e+qBen/9wdWF6uhq/3ISnKr12nK2r+YuN1NIgCPZ5rVUKUdhdkx4+3sqSxRyI/KBKmWl7TRuJtwZwkqGJCWo789S0DLXT304DDUJAvphLRzsfquajHXFiQhpJG2szfD1FtUcNRB3QpCjNOTs7MAGV6LU/u1OZZ0eGBMHxhGmpZ2etMnnxARW5sIMFMF3P972KX6npOfSq9A8fofLXbx8OO3wENYcyfd2W79C70vj8/+KmLSgV4nev/vnix1d//0mVmzWjQvz/hlGh7sZDfunLJawYFTDHOe3Px17gZ9PJhOwT/Gng1IIxgGJeKP8Rrk4NbR2AQlkTG75XE0qBDvQ8ZzGgOCa+otkFDQHn0CYF0YiPYUmYN83TeJ79CESyLAC/ywI+sDCwONIQkfSRSmvxBUanbg7LBUBB2kUwL7+340BWiLgdyAqB88Y+sagEfowqIBUhR0iiadsMBlmUti0mpXIXxWDPEg51OGOoDBotFUhex85PdVwfgy/MunDxr2/fXdzcEFdPAPiOd5gI/g9gIzSjdp3RJxDHSRRpCCooFDjVqRzJVFGP5NBo2u0zbgE6BlSYTXcx2JsAYQ7YTmMjiAcQMWwyDwNsqMOLQXyr1ZAQDndAPpA7oGhnyqwA00iIWc90bqKrLTQNRacyAm3BwcBlBNoRJAwbTgYQRP6DtWTRL9HbVZL2sG8LDgfgYX8IdcPMgjQIhUOG0NjoHED3FtEHlSPoGuazSjfWoSHEAoEVCqD4aiS9JAC1qywPELDXO7QEHctmIFauRfS/hc4BVJYOCDkb7TiKgdqPoxiog7V7pGl900zVZgMQU4GrOa5cnECndwhIRYC7oBudDxS4G45oK28ntpXHOZMDfcMk5LH8HHIBS498iN9E2hT6HPbtCzTjh+Ma5zWwxkEkDxUbH8sIW42TBR4Nq6my1JvBFiayqVIjZaku9rXgIQXy24KsYR3Hb0dQNEzOxojHddQPzQBsWdr5txktxEw4fdsNNhGOTOptnC9DJvWKDebMiFnKhQmFsk1rRufrZFs4rmF7i8c1km+JDJ2BuGlP4YAJRZpsNy1vl46hdKjYZFvqYij8KerkzYC0GG9sXhzpMQ5kXhw5VkGvNCVkcGeIx7eG73AWrM4ZCfeDOJOCP3s44KLARy98ge52bVAFxhVxSqd80FJ/5fYQTohZb2hkU35I326MJ/UCR4QHArWkwj9tQ8xa2zjAZlGOa+uN9bh248hmz6FRy1r2vEEEEaCwvprdS3ntQbWcpcA8mtuKKIJ6NTvSdI8Ejb9+jndAhTc+ek9F8n3kDmpjlNqKQGL9aHdEkH2HyJD1NI7ric7bcT3RmQXxQU2WmQXxjS9A/DAfUJ61vMVYcT+wFj+gGPQ5CgKV/qIdGvpj7fi21nxiL288gkZitoVlO7BzuWj9MPfo4K+Fyy8a+t1pYaQBFQ8e1ROXGSfvCZF6Yo7YlJP66UoV23AB5hCtn8VbX6ahxZMxDuyj3cE3mE7Er2mFVc/ABAQfsefVYIyg/Efvznz1N9/f/atX398t8N+uOzXTV+3UvHl9eXF3nZy9PX/9p7Ok9WlSVPn9gJHUj7LG6Sfp5ffz86ih739EHdR5vyYlqwGAOG2X2nG9kPYXUzxiZ3HR9MhN3oqL/kdqD8oRnYnbkzAgZaqJzos2xdk2THchsOHZdBfoDPnTpv6mzPfCrJctiWvX+ARwitA7q9JjVJCeR3FiU4QmO6/XwFgCPz4Y2kyAmI4f+Lw96X3AvOdttqZ4Kt3Z9kXPIWYVolFEliY22lsWD+lNfGxXH+RHVJbr9vHgcR2K3ECSiPQUdlXzjRoTTWoujItGTchucRPt8ZAx0WEqTAz67M/MmlGz9pAKPSMeyKGmqvrPMJBGVdHRhb5oTeTG03R+YDTsRAOUrxWHRtvrIDYrBrvPj/V0AWnRTJoHCsjQZYod8Cy0RUci6cYKZ7a5is8bR2/VoOoR86GfpLUjJkRvM9XE5sSwmhDNXRwVeJpH8f4nD1JRTQqgZQeT6VOLKkV7QAaoRRHwdsCru6hhXxCaFglXWhAXUoYYVgakzWxT3V9BevCCyr91wGTqz0RmQEcjGAo6mmXWlSJ9nLKnIl+NtarGAbz0a5ONA0lSVHqELkJNVWWLQ1ryRAcXj+h13GacUE17aznCknqxQPm5Rpsc19jIuXTS4nbNjlAkuy3mVJMBpi1ODQnNlzZLGL3jgzpu/LQjTDSZsGhG5AJMIJ3U1cAPyIN1LvBb9Shygd+qXZFzd52zPXzW9KPH6PywafS4mw/zPlSX2ZGbc2jGNBZdjdTxBzIJI4rETECXI+yLEBDuEPOA5LkdRbsNgFwg3gNpW8SiB5IzZ/J1PNSce6VGV3OyG9IJtolRd0P694IovCLCo0gfhfREgjGnIyyL9FNH9ECGqbCqXRKkNbyYcb3ecQSzsInuLUMDrnH/VqDOwk3zSZnrimQOWLlFWiBhr1SwZkVptJWzZ4F9biFta0F0aZnrdiSzJ6TFBQ4KlVa9j9xb7qD2xzDhSFFnYju6RTFkjEIfJFp5vj1uELb31WqxmDyNUTSpyI3HBt6MadX3SL0ZlwOxqTAHaVwiBmRHk+wmLhohqZAS6YF5jrsgUq2QQ0lRY+pBpmRjwZUjK6uZxluu75FjPkLmY69zOUhDofjUYhGXic21I5dKEt9YiLFl8YkVuFHZnLFB0MssZeLtM5e1EadQqyRqahjkNkumFldjsxmHy9oE3iAZUfaogBJGlwmZBk8YnSfSihYBpiMna+dVqyITTSIMOEWym7RqWiRk5VXvIhPiIRQxpUrRWV7N2F6Hi3nVRkh5tsr2ErCxXhVGboPnvKyaATl3U7FmB82MkEe+NmG7HDJge5uwufD00C8WdNJaqXkf9tzWJl0FSMoPB1aYFrEauE2ZMwmY3g/jpmJKJG3fxMw6krVvUZONsI60pK0JwjvUJLNsq+ndROTbDhndvU3YUdhR3WEmq0u3A20GrFdDqFe65tUcdoyuujWkByGI4AyI16ZLbm1AQ77BF5qzpKJfw9hnepU8DsTh0sm3FEI208UI8h7IxxjbDrQBPNocloodNIN6tXnitJeWJy1q0uz3+UgilYkpTDMgR7Oi3RrI035ocSF9GocWGEJjW0eXHone1g3ilejM4Nw20tEhMN24qb4pQLMj4hbFzGdGlE9i6jMjzduauTlzaEV/FRCa4bhpjEnZVlWKAT1h5AMrQG314gsrkC/PGGiTA1ImMcgmh7zKY1AtriC6qxYHJFFikBhm7M6XGKQ0uyesiuYLJaZikOaO2J2FMWjvLt44BmnaFRmGvVEBps3TtANfcAGmI04MCZstLWnSHN00J+WoppmMo1nVAsyGnBIx8ANSXzF2JlRzvn1owaMtjaeVfqjybaRFp8CWvhFGjxTVGjI0yjsm0aCGvdiSQdncGVFDVXiaiQY1EOaeJla8NyQiSZpH4s/v0K4FICMY8anctmmYy76DV3CWnVjGJDHnDvEBZTHpDjEZ5SwKj4jwIi4LwoaVpUeZTekTs/Qo84R1SZhNS6bRYjjCyqbdFKw9iOgMmw0wlsj6TYiJLKl+E8oZaGANh06uaJgmZzk1RxeRpE5RHR2W7Spd8kARSXYV8r0FMRRW6r1l84jGKr63PHHae8umcI1VCw893VRoIJS6qVqvsacrg2T3ZGiETA6BctU6rGyi31glMlxHNZavYqoZy1cRp5pFSRjyjt7BJbCxDQ9fPY4iR7872L9cXn/08T++u7y6kvnRL65/+hggfLh2ONajzY4dd5gOaAHiYd+PT0hY0Lqn717dE7n//lPUs6Zyz1+Vyt3dd5HRHWhYQfKO5L2UZtxuLA3uttPzKYj11mnFyn4MYRq3HNPVOJnprE6J2bn9WjC0c8LKnKB9Gk9NibYCC5CYEoqFIBKsQDMZtZOy4miHhGVbzzwnbKdEZ2SmjLYEDvZXPIs2IF81k4jwsPMkZHXOv45K5ZjXuTs4SncwPu6UoGeLUbTbRFG1U/YQ1/YQRVNLgGjR1OycRdWE2Ykr1dLsBJ3qkewqxNBsYUimwHKsTxeC5VifrgPb2JttnrF8AOP6Z4qwteisnQWzhsSTrCPU4nSEB1SkkmhQK2J17HpGmOW1WhOyoCyZejaVzgcwq88WNWvm1AEtk6SlTf+e2cMJvBbZDt7Hw2mTlIsh34o/nbrri/b07NRjZ8WqTr112oJVnTJKhAc9SMcdmN5etDeUvQCa6dgU9Vog14AvZu0nw4xIPLE68MotojMyn3hFDO7MJzNbE0J40KoWwdn845rDOYQYPZtfnA9gSbePYwYo09GTbr5Yqmg25oul8pPfKBp0jpARqIdX0W7srxffRL9JD1y+e3N99tPF+buzP/98cWF6yKbZUjqCBH32/U2zJeD53zRbAl7/TbQlm7lbsyWbU7yeyKFV57znXA7gENbzNPlaKsO9IjpnYr5DeM6zRTSW55znVO7hds5xTuUHAC2zouWK6JwxibigOWdk2YvXTiPkqnMm81M4syLAaw7amvnpLIn5g6y4IDFnXB7r8YbkWQDHMqSnjb2sktHYq1qUzwUMe2gWZEYOo0my7AHUbBUIqEYMcehn5gjIGROy62lbkIQ5Rd4oGWQhDZLmHp/qLOUEkCrqJtV20oqGnNAyIVpqluSwpXdJWIHoxllyr7ogGceEli/ALv5gRBViFweFOVTlUpiGwA+C5HEcLSWXUxEtpZKoszGapfQVcTgjrK1ow0/hn4qHkIiniaJRMxR7CaPmbQbrbXbIAuZt11Z84sBb05EhmQuQxQ5RcjMdkdwUQ+wr9nBClrOWQ7JDmw+KRQo8vhEdeuxwGg1YRbjA1+9DhFSLblhNs1XU8AHeKkrGAlSeX3CCg883R03JPpwF7NILbkO+WTMXB36XRXtxOK5Ze4HAfGqfaYCIzjW78ZZTNBxnOYvkXgObXglZsyNnDZp09iMCuNXMKiCih7QE4RjS7zyhO+JZv/OENCrQfaXhSzB+E5xZNAP4o1EtOcApac5qVikyDSfxfp+FqRDNSCBUumYk9tJVCZGGQHqDhhBw1pPuHM0TIiWa0/tR2jGs3p8ZxRBeb1Sao5uGQfN000qXUL9J1SwGydFUzWLsFdUK/86CNqnGGZCUzw4IgHWA7piUTuyAvI923l6dv7/wex/nFN+YxukwjT/5izfXdx5j2bU5JxCHNM8HK4412/5hRVgO6Z7+osdk3SL7Dw8r8Oof71fg1X/5uAI362bZ8nWbZX/b1sy3yHJjFh1yD2ryoy0jafPwbGHUUHdbRBHHZsbfTCr87fac3vmKECI1dfpdhMbfdW20JFLQFod1xkWHLKUn8nIN4py9CA3aiyRgN0Kz8cRho1BKRxy1iZBsF21cKGWF+ojczvwVFfkaIH5/UThCQhfU8e8QbZ5op0B2NWpDqgOQrI4ig/IZxocpDVx3GCYlxuQzh2FSNEfKUCIPlgUKr1F2i/fSl8RMmlsMQK0vin7RpvBNmr0FoNyZxInWCKFvYr1iQPpSk9g/D7AE68N4OxKFJTU+vU8NoqaYqvhOcGhvuBHxjpAumoOZhkmqcdmdmqJxleMbczebyyfP+3RPon+J805d7k4AUn1ZnSXPmEDWCPe8A0KNjndkNNo/AjDYrEaQSEUpyxEk0PZVRKMDaiRFNLrE9/QS8UhGHMTzGt7amoHqXRGDSTvDUlQPmYGArMiPOubpWORHHfCwLjydEpAeel72Iw4ekr6om2rqQDqgivYIJHXYwuCGc79U2hSBjE4VTTEifcO4KUIpQ3EKMGeKlQsuzfdt7WK8OxYtxNSRAlIbbRPD1u2IvuENppNpGlGzvZ5Nfa4BOZeWxTgVyJ81MYQci1ZiIIK0RTTRNTbCHBvtvIBHddMsCshdPB/ni3vFBhVNtGkCNq9S11wVkGrq4msNyLX0fAgbVgTakpcygXxTF8e57Y+DGyP1pvlDxv7Ugb9nzOO4i40yCKXO2ETZQNva0BwekEsZamEAyKUMcYrOGcDnrE0ORqhzhjTI3iYLYluew4S8aohGyNgH3QqNEFgNzQlCLbayF0Se5HRz9AYzv+xbpSkH67QHsy82BPm8qRUBKN+zb4+mHGxCWuS1eTtnCNh8E6PRjam9b+rDLq5aqSHE0y3SOg3jXW6P6JneYL4muoN6g7lp6H7qp3fs7aKHmnvEIT3ponkhbUtBy49QFrCDn3AAu1W7NYKwO7TLGtmtyNISbEjrdhDHZiOyJRtDWgxZNMmG8s+EmMVoAZra+qIte2VqCJIpSgGkfdtEcVDcBoEAxedbYu6EOE5j6omLpm1uYiaCYhQtEEEaJskCEf+ZJAtEot6kWiAy/zEk0gCRaC9JBhihHm/UAKFBjUEcSUwZYKLiyLJq+qZC2wI1fYMBmd1slyWTQh6qWTIpaAxQlhagrHq+mYAUeU3mKsWjCCFa1kJFZwmYSNERMTRfyKRLX3R5L/1VQ9DrmjVVqNcbO/NIi1+RrAkhwCmqg4KqKvv2b8YIKrJ1WsoD4UwrjfOrDZHZNV+IlCv2neKMK6Tsr26akQfqFVzDIcxeddFaTslG2juqmPpHGMlq1mQPRLY09RshwGIb0jeYh4htTg8Tai22GT1MaIiqZoWcgbTtRD6reER7+oYzKDXJK0IsMk1LTjo0Mk11hQFKGDTNFUK0Sk3zhQiMLTTSGUL8aU30hgHLTLWhcvis13oHRAGehN4cTrG/2xMXVZYd4HtFriFItvaWcyhuOg2S3GySG8Cau+TdINqt3rRFcY5GV0bPQyxULE3+dPPGJt4IEbl8R5BezM6ajkg8mT0ZhzITseiQx9sJALWEUY6kVxs07D98VvUpSnr78X9M8V+QuGhopuTs2JCEmYsaWYb8AO9+3GCSyQBzzEWaN//xTHWSazHiQJFHkwDyMXGHFAHd9xPnkimOdlyf2buAcChuGCR5m+qIN7WFz8RBy4Vkc/jROAfeZRBxQv2A7z1NqA81vweayxWhbsCpSjZTVdN4RUhIMB/gMWQxaBte0DYxBJZ3/1F1m2shVNFsI/JgjEFzZdGeRx60rmyHsyJoXdmObhrFCMQwolGMIGRIMUaJxe6eVedEQrWb93f+jifei5Ek3tsO0/gZDRquMUm493jQ4l8Na939hwM0dfWr0tQZIoLGV3dCMwMS0O1s4Pz1n44BbKcDQNW+1/miqOJyAEYeWPYKLzuOt2aL2NNpsDSOH5lpdsCAWceuZmx42GqkKXKTRMcjubp4AAocYcdP5DsDqUHkLzDdNRTucYHoWblwHSq/iCNQgTylNrZ0LZgdiB6QjINa24EidS0Yvp3z1oEOLvB8dXBuFfn4fBheMBYNCA1Q1KFYUYCXDh1aun5AdG2EKcNHx8TM+WvxgSetz8ZmKQ7ct3/tT4/Kgyx/bR70JBFg2huWNez1nHKPIDg/YDG9xxO8nFUbnPrsJlg8RvN3r373pOirf/yk6Ppd2r4uffpjFPyX4lEPp/Oob0fSqBeMRv0+B/7ya9oT2/L07fjx12fv35x9igkMORVnstaGXjsq4kWHhGvYtQokx1k94MUNOHN8YDGY3qrsv8O2sNmjEsMYFYL9nMSvNJyzfveenNuIneQPsInZYw6LVJf3lKmaNEc3bbyvpxtNT5kI9vQhCkfY0+Mm3lhOaUS8XTgK7sglH7CFSKRQZOti1q6sF6VI4crCG1QLQf5eSaFQpbZpF+B2/DOzTk/3kPREGPvZV2Cd3Ua7Z+Di1u+LZ9kyNGpTMvdEx1gQL3oW3UpbvJs4af2Ix8xuVseckRyMAKH166KObUFRDsZ6CCQkb1q8hXw/20RaZ5+fteANwQexXaR1dpTYxtGGT4DIhb8vEEp5Md7DaMlhaSaAMasmZOs2RGmmbmVTYzCGO6youQV7CQqVXHBkJDHYyqcHWyVrmQ3nS4pm+bawqlk+AAcsTdPTPrdd0xO4SspAAyITYbxr/lz83MxTsbTgT1ePrUwUpdm6CSFaPp7+++lGSwj/NyzNXsBKB1XIFzc1AwBgMXZ9mmiMBQB/6mBfvICqeIdmwWdoNDH7hmwc26FZZ2eL7cls+LCPJgZwAFFPE91NWjCALy7LuGD/XvzcTpl38Xa0lRmiNFO3vvF3LZLQD+rNA3Ba9UhflojKSbzQbfSb6mzsXSpq+MwUSHpl715ksxrtJoCyC03V7R0EIazug/UmAJfaUHML4Xjq7oaPgBmiozJtZoiOCpmTnDU9TWscQm5hO4Kb++kq3xbs3JQ0M1071GSCrZqYTLDrWtsmPvMdcYF55jsyovbUHhCXNnpa7aLiJgZpCDB006I057O1pABCULM12mQRRp2tg4FhW3FiUxbbjyHHLjPdQqADQ4R0K9CxGyRVjN36ihObkuYspBq7UVxgobJhFkLGF5oWZrUjwC1di4ig8zKka8s+LixagbhfolYVsg9i1KpCjSPMnt+DNk1jRCE9NnFDFKuljjJiUFZWjNfcQxihv4hikOZ8uBik2R/Oc1oneBbRntSau1MzRTXMQnog1VlID3IQkgjpOaDIRFBeF3hs0wvO66VQhJ80aZUh21qSVhlCuGWT5p2WXNeMnhDvMwrpWVJdozG5TVTFYhIeb2xHLRHJk1Z01mgYTVJZg2E0JFXEn+YVjTUlLa0YrcEAF+HtLmLaDVnNogVv+RAm64ZPMCta2i0JXNbgEtgnoAhpN0RHFcJ9ixBVwym9W4SiGgZC3y44qbmc3u0x7NOBH8FU5W6h2wXfNNIudLvglWZire2AUKuKgB7nQ4qGcP4CTNOEuVYtiIuHcE3XqUGAgB6n9QPE8zhMyRqcx1ElsjELRDCsRWkYMzR4lTnfW8hgCvrcqr1/FQJolP/zdkH8jAVWEHs5y/OHkNtqsRqyWT1wz02kMbBLERtSdOlSecg++12K0ALE9ox9MURsAuJ3HMJlEL6TDqFrfri3HFW0is+SoBm8pxEO6RHI2xASGqUb61Ci5rTiaabuaWT23hBLP9RAtEGidiDFWdAOQlc1NMxOOiBwHiRkJyI0zJo7Skj7q5Q5sLlyNskNIZw2GkrBZnHQMAoJ0ZLOGjiE3lqaIEPUzaCwcrtgbGauQ0czLUdgaxZEoI4jjcHpOCI0mI4jTDr/GeJcxs5/RsixpaCsIJKr8v3OWtL4HIgYHITntGMYlYs/iZAmVH4QZmsWaWwOAIqIkYXmQEK1WK2uqIcZYc4iisFZo0ghWFgOgMqJUUPlHADKiVED5UAnRSIyQK6BJJVAkWEJSQrU7MOdJCi1fbYTiMexBwAkEI5jc/CL3AWOKlrU5Wimpc0czbSoy9FMZNNBhkNkEYgDMcmIOBxqwGLMJA4HWhUShgOthobCCQeUhmImUTgIA04mQTjIVJUs0eoAWMmYpVqObdxZ8kUAED0WiVPHvjUKiL+x+wJiieDPbT7fItZzHGUyG9IiUy1j0ZyR98ks6gbTUazmOAup+SXvi1nUTYD4urTUGbSaVQrJnMWsUvIM6ZyJVcoeONtUpfQB0jgXawFvCTv9WCv7sLc7uGPVUmaeNK2o433koC8vIAsf23bIDFlbdlDn8ZrStPKOs5ztmPm29nfL820B2UVLd3rLUMVhroCmTeL1h0R3ZrKh8+W4PSXTnEyvQ1AdZNxI6bnR05tEAw0gfBpRQw14emoOBykrdaml1LmYOu9+7AR1pxPUCH9KFMEFnpJaxtoZYTtogGck5gJHAmeQzQU1jVwEGiDThuNI4kxfU5g0pcXbKm1OCzJtmOUoiMR6SrkAyK6G1J2D0LbFIXXn2KcgbVI6wD4FaaOzcYWY3Zu2yBpsRTiGExQvPMa2Nt3rxo4TQ6hO06bGc46S5CwxbPnYWlDQxnan5yiD9cSqSAzETpuUcYOWKGg2Vm0Wa4lLBzpuQSoAOYeNGNqX4JnYiRjbF+Dx1YkeyRAn46sTPrLv8yxpQMem6ehwoWvuCeFsD5ohARn2FDVDspdAozbwyOGjtDv2hJsUtfyDzc+fWNDCXLWi3cLQfPMUpbYdZOZL0sYpINMUkjZOwVtgKbpztn4HPMDG/sW/6KhsS2NyWF/82gPUEzuvrx03QN0ZhIarTs5+rwerjs0aXM+w+4eHhcAnq/e/jgl2f/zw7vr89cUXnaoePpOUvazqf1ZgMtgO5OLc6KnqmCdyaOUyS0AARILlQBYvtv0umGRydT0BD1zFAcxIB0dBA2HgkARDlHfS13dkKPppPGvxSC4kMCkRZwpl8aoo7FURxDyFo7c0+hQittEsp94eNuscIjMbkpY2EHM7kFAlhi/A+RLjgaRf9Lg6JKOftYscSUAXaafLEePNE0yzpE07P8vHz6GLs8M52MMJFJPTdiCDUAoHcqnsXh3kxFr2cqf54oBWzCTaFFAPZ9HfCSYVSVIhytk+zaaAMnvSbCotptidzteTt+MYM3JgwqNtMaKOillsYeLD5oiZdLNv1GwhHD+GLqHcEvlAZhGR/y0uptFR9y193dKEcHBTPUsHl1B2DJYaLvpbViRDMneMpX5LaN95KcfzU5R6HKNEYeejAlF6ObDFu4imRD9L68ZFLkDgUgMnEhk4JlnPam4dceSBoKJKPikfMbYuoY3mtR5HHFDbcU3DtUsXOvB8rFKiwPzgJmXVzO9tkpsBsjcsxjuhRAItHdfVLfLCmenJHZwbiHLbYu4cFeW2xRQ64pD0IybSzT5RSjUDmea+KYKBBF8na5yf+61Orrud//jr+fXru+tnXnX7FqiWvrlz1j/cHaQ/mTKiXXLs8bim5U5WL9Nhq2jV79Ci6w64ThS4j9BaLHB3KUlp75j0anxRbSIKwv/1YYOWBeHxVQvCb9+9+fHD/fed3by+vLiz0LO3558si68LEwDss2hTQXBH9FFWO/2MThYCRjUwcN6zmOe1ZdBrxpm0om1NnleVKVl20MdCucKTZs/5Apb4WhG8O/lbLopsaF0jZ/E3uRr/JM5K0liEv+3+4HICligbIcWMWkI7LirWlLB0TOn6MaM61D0sHDb+LCIsxJUVivD6NqVv5yxuR1Sx45OexxWubc3YynWYaRZFKwoIS0kUZSMsMVLD15mNeNZwwGc24pktXYepMK2y5nxmU++OIt4duzI2Fy7YrRO7SjYSwXjNPuKprEgvgrb3dVHIpvbeFibW4ewlzNpn2sJY/4I0m6YqbjLQd5k0N+O0GWpuxhGmuRm7ZzFrbsYRFtSrRw09cWayx60Hihy7ajZ3rIB4NotOKB1R346z4e+5aifB1ky0nrioY0MOYVsUsEknZYorqn8Jxxeq45Pow4rT3leL8dh2RIE6TD+ziFdPF2+eIvoeoMuzNE30OL6OPcPOs9PMwkRWPRDVXrXwzJYVxWOlxtI1kQ4NmLtaMykT4ECu5TiQdtU8Tzuicj2DuLMcZWEma2g2DUSqTXRFAOylheMAyi0eB/Zumh/KR1Ssw0wv1QsF8brY1beR0CgtytpcZJQWlW0uYQucxnEcALlvx8GEu+Zw4hEF4jDTi/QlQEa3ZzHsBUSX4zDEXXzLmLLaYXjkLjkUW5RY5VEf2mMjuXPWGz60pDTwyB7HAXJFyjFTVD4M2zuk7LMtivIgpgcdogOxhWn+Ayj3jXEYiDNs22Gg1bBJ7sORJbkP5xuTdsVUuYysORcAKRp2TGKMaOBxFbZ6GHQ0bO0wRGvYJGfj6CU99W29gshqKVe2Q+BcV0NQCWTeuSMy02GY0BCkR40jqxwGew1BCswcWVqaDCGxD0HzQgPZ5nEYnjBoIAFHlvTWt1O7IUp+yBMmOqKQ1CsjigA4OzcdYqEoNh0hWsIZ4dAOUfI7drUmaF3t3ldLnscRliRzcT4zkW4FKWiHpIFpkKpm0MABdn0vJOkxY5eNQyrHUTGHpLL2m8JEME2Qg+DnUIHl1hdk67Xsc0DiXw1BYONXQtackCMsiszZprADucfDDgWAOAQ7/slFdFKOONG/AKMlQ9b8i0PrJD1sPGFSNOYsYdFMwREmgmniJkOARecD1J1D0TA1EMq4aIk1O9gs0mvHhmKGIvkiTzMJU+Np1sXTJcfUZXCODcKCVzItDbUc1HAcYXDQ2tudHoMqOSFPmJQj8D5TS7BFoAgdquaZIhIUa3gBp2tDAwx4wiTPZHdNhSZ5Jk9YYKIXu/cqtKhFL544jTjFEaZZhiOsSIzO9YBm55cXe5AbnpnORKf5OezQCtBShMOWgqCK/vaY9ueIUTf/04d3b68uXv3+05Iu+3TzdmifbuL7dF9f3Nx8FPG4gnx/rjPFss/IDUnCgPoZPHV6Q+7zL+YbcbFW/wrzRoKvxxmXKf12TBNhVeqXLn9ZGvVZ/y9xGx2hNshC8YcDGKtnyLch9VI7xWLFyX39IxAOBDgG8pbqfw2XFFt07PAlFTJ449vhbwF/HRcU2uCwSAR/SI8gqjC3KtuH/Cg6UXTay7UopsJx3p5sxhU+4cQLPoN9NuHZMT69kfkMJ+WNB0IvxcbleETf8jaTpXFxpEXXMrYXCbY0rR7pqNmPQw3HQZ76YsJfyUO/K1syE46XLNyz4LHORETJZyAU05o3QmihM+0RAkW2PfVS9nSqCrq4gDBoT67AgqK78drjWYFx3TSJdsVZmDdJNELDHDT3dpqh7wqWd+Hj6z8Bfi1Q7cyg1O34PuZtxvhdjkPrZ4kgajuuh3lbdTAvtsBkCq/zhuaT+yXKdliPQwnwRZvgI1xguGVDl69Irga4OthqZIevu1JYHxYo5m1QKEK13jDXZh/Gjv04LpqSwdcOcPyq5meALWVLjQ+SgVCqRu2lBLz92Crk5u+1VoMMRxBrb/4h0hqS46IfGbv5AZOtnbxikT0dh7VMaVzaccGlzby3TssxNNLZADvWJGcD3B9NcjbA9dHAl01asG5Pf5yPYNl+uBKBN2DTkmxArrMNpiKTFg3JVEXmwI7kdFxDckb6kbEbOi1YpAlRecHvzKSxgIbDXg9rEtSakPOiCZnJJuVFF/I6mWTXoDblAgOMfQRFMGDrQytGf90yz0hKrfMrlzpHVurKX72mOCTAz9dXuyrly7/ksV6gacp3r76/+++v/v7Tt7/6xzc/fri6ePU38dU/X/z46vs7q7/52zXAJvzVAmwAYNK/vn139+vnd92O5v8IIRmH+iDwlBTmiB8On2JLS2gxeczZ8OFi8mBmrZMF1HHS66by1WCgq7WpZVmEc2Z1NN1q6pijXkhxaVsgX6iSYOwnbSPNi5/wkxmiWG/cThjlXqcyUCxJ7KeBSVYD2dEj8wT9aNRUdlp8p6Al8PUFULOHod5f9TQEyMbWKCNAvkCMdi/2zi4QJejObrNzvEOVsPLaAlmC1huhzy68WGSPquwI+oIon5XXFkATVF6cyhtk6vjF5ixGuoP1OWRvdqgS9Ns7fkMmMiOLLQQcFNbbNaBk8vOy4MOnL/1KzWqnxQMTI1PjfQoyWEFwVQD7UxIQkEDn+w59wt7GZcGez8qriwnvXFAeCwUqYS+7upj6zsorC0wJe7mXBa6ElVcX4BL0GkZ2pdMuo1JIE+6JU06K9Mom+xTgCiu6x6r8SPi1x6oUZGXhctKCb3/x87xg2KddCpBWLyd4LKAcUJr6XDmtmFw6lQyLq4nxXDbMmaGoO668INxn5aXbA6ErZ0jHd03yRZ4WCBZW3mo+POsI8wLLwspLC0ALl3jKFLAFSBU5+g0teWVLa7oPQmADug9K1Lz4xX0fFhz7i5/HBa0+fZ9z+BRaPIKrERrIEByQ8G5CiPE7/8ABMGFt0EkxhOdd9z5xgXVh5YUF4AXOXiGfrfuduEDAsPLCAgbD+okVBT8rLy4QMJzfAeynd/GBc1q7Rx/0GxCh1dddVqRgNaz0QGFrFi1RY4FyWbisbYE3oX0K0BgwCt+CBlTrhv5wei4+z8n9Ud+CTA0eXXyPhdPK34P3aQgF3aY7tW01FIAU6BC+bYJbg75cTgR6iuqZQGcp5VSgp6GeC9xWtP9o2g4hMd/obCBE5bipTdCnIh5kx4bcc/sxAaxn2xDxdEYQ4qlPpFRoh2G4RVhODFj8fjklgHZpCODiBMRFQBAXAuQiIJCLQLUZeLsTxU4DV57KyRGQqRg6hsJTVwZROKc1yg7LEyg/xbxPlh2WJ1CGTnif3OmuYejwDO3RCJFaJ90fIZeIDqmA7sAEP7VsWksYPuGQsyf9sRUq8n16gjAURD6fIQzIpKqkFqrCadXWkPgkIjt7gL1vHVr/TfU30DgD/eFVVlMI2Nt8OYmAvXyX0whYgXU1mYD95LqaTcA9OfJpNpHFpCNk6ZmqdnkrI5a7HHE65gK6NHXQBXTnF9in2QSOMMDCmRZTTvBpCKPiCRCMgDAFFsGnIQTupdG8Ugh3e+HpqpAZTEVFtofToCL7YQYw/VPm5hnAciHufP1pllbjDViBeTXigPVseTXmgBXofLJcJvM0bLLA5VADNIuYkKEOdPELOus6YAO6IXXEBnTBs9Qj2FLDSA5nbF6D04FxNQGBdlsBWTXBbQVk3Xi0BjRtsXV9PRAu4cbj3QMyF0dHbjgHo8uZQ2Q2UNfdk20HXS91OQJ19+QI1B9ezg7pmUNHIF3qgkyq8/lD6Aippa7TgCFhbKwfQBZJx25A186IsnjklofBHXbRdsBOzK7RjhOcGFIfPAHGgVQ3h5pWPLFoOzj8u7f4KgDelhd1uMaw5amObCBM+7ofc75e92OOQN2POQLlZ5azP/Iry5Gn5gWBmn7cNIIuT1WGosuRcQLyArj6oo68GIh0NCloN2EH1O3YPeZBTwl25ON0r9QQ8fzLCsAxxMA/rBoilq9gQdQQNGQQooaQXZB90KJct7LPfeTxgtBny8UrR03Z9TjLKL+gHP1kz+Po17QXSTuNdIXHZ0CHQYRnIHeMjs5A7l0dnIFcuzA2o9osMajLKvbPdZdVkI/TXVZFxPMuqyDHiS5hAWCDKGAuCiJWrWCdBiGJOiOGfZB1LIZ9soX5Ksgu6lwYjpqyT3OWUfZpjjw5Keh8L50TBMgSIs+FgdwDKhfGaUCUqOMykDtTh2UgFz7NhYHsbmGB78jmlpNGVsZSTppxfAIDRka2QQe9J0Q8j3nPyJZwkHdnUKGKeHfEyW7JkScn+OyDtINRcNnIjJDwyc7J+XjZOTny5AeXs5gy1N2RR7+koF0RX1IJmcUp+x7kWtIRFcithHNg2AyR6ZQRjfEEDoyAfNwJHBiI+PolRmbGJjQUAxbQ5Ibi0yKzpsPbbRJRFd2ODG0+gSvDVlbHttvydJIMW55emLLl6SQZtjwN176ddmRVKg3EusXpMt76UMUrZ2SzzocBXJInYCoQ6fGE+cIRRUzYZ11nw4C+TPZdSEVx0K4LKcyOLzByOQ4WxI44wyEmAOOJc8q3LzBpOG00fB2ZSi2jKuwBI5sKXbdHg8uICkec6rcccWoC0Fk7FbLuiOvHj9xMGwtXR0522A6fNJlkMAVwa6egQvw2RHg6YeRkQjkrov1r2R0l5Mu+xIzlpLNYRERnOp+XEJ1paDrwrEkylsI+SiqhRUJUVd2OfW6jWnpyvlx1O452KozP0U5N7TnasWUnxCijRiWYTgu8IuudkKOaSO+ELI+Mm0Cu2aR6J+Q+REEVZtY3odNC7FkhOmQCmeWhzwrJiPTGFHCc1RNze6dNmkpJRJ3bHyGDIxxxopMC8CMpq07KPr4yPsIRpzopZyHVt5EjTn0bOR+r5eqA0mrKWqrO0ZPJ1DkTkdREHXLJyRAH5I5DeSea/d2gZ7HHUOmcE8jgrUJz1iKTt3ieCWR4lA5wQAZqFRqqB0DHUmGx5QCsNsnzPewTxtNHQEqqPsY2oqr6GEecyunnLKH6EHK0U32Mo52GKT8NUp5oIAR0qjQcBHK9yDAI5HaRURDIJY6CIMzmr4RiILr9a3p49kD2Qn4QDWS95AdRR6TT+AegSTQ1toyEDPVsYhnpNIr2JPNM2Eew0/4LWRuZXcK2MplbwhGnvpEcceobydkR1X854thEHnLFdLaK1BGhGno8nAb4SCydBLI+I3AykeWRGSSQW30kUThyqaPsEjZvSxogctxul0883AFpRE9DflAhhB5pyDRISFd5GhRy3N6YvInIcU+cmLQLGzKOWEY0OMqqkAb7lGYZ0+Dppz6qPP3U8pInT4Y1ON/LPpiwU6O9mBACtSzzRiAGnnWkQ0Ckg48mm+cqo2AGm3QxB/rZhDABZh3mgDBAZX1uB0K+mAP9ckJoTHMQq0vhtKJtDkN0BgAkIsuQCOdAyvQSjn1EFTnu6acixz15agLQ+17ZWTnyNOT4iYToOWrlKOhyigwfurcsGh26I03mkIBuSxkLAV2WKIeETSOcUbiDPVIgJ95/ASXLrAMhEHr6rCMhEHbfrI72CKfhGHKiIeZAy2FObOkKmRaSZZSEc44zDx6H1JTfWra97CASqJrIHmXZizmrKXsxR5785HKWUW3W9eTRUHKAZiBnFq0H7bUMn4CuxkKm+6CFKGq+D7puUVyFPeMso2QQ9iSVzCMnQkWWjE/1NUSsnupryFboqb6KiKdR5cgoqFzFVtzQEOHyg8o+rjKewjm/VX5QOfJkV+R8r+yKHHmyK3K+t7GXPHRqNHh5OA2rkXewCtAPIneZjKiA7hwZUgFdOeykDmxBUI9l1kEzOqfDHmuddVAFMs8y66gKZHxzbhTO3FtBMRUYTyvQ5iYizZ3PUDkkAoA0yTKHhHPsZCCF9/EqksLTT3Zcjn6y43L0k5txHf3EFB9Q4M+dSvE53yum+GxpMjsEdCnJ7BDQjYqyQ0S7PonSQ0S7HDnoFB8y0T4P+t0UkVKkPl4jItVCHSoRkVLnoMHnEan9DjaFFwFmhSIP17DPaeH5HzA15U7cYKup+h3vs+VeXEee6ne875W7cR157IMJ2+UueTPkqiqbhuaLpzGOFRlVgVw1RUZVIPdkQVEV0SzIFRRVEZP9c7lXKibk43jXBdRFi46qiBHRmkZVRABVUVRURUwnWgft0wA4RYk0sxEmVn1LOQdcZ5ew7U1GVXj6yT7N0U/FpXv6yT7N0Y+mmABIFEpkq1LQNRNFb3YaoqTQLBPItySyWgUtu04vgTiPpMLToUse5Z+IZp22JMKzPQgqtiCeCSkjW6M/vwqyenL5KmZEPIVUd1Y2i0h1T5yIVI9AqbrI6AlPWRWp7hx3mWHC009+ejn6yW7K0U9Fqnv6iY8uADNQaPAEdCJl8AR0f8jkE9D1IaAoHm+niohPsviGrPxLW7vbzIt3nzbV+wPF/gPffvP+3+4jj8vrtx/seKMU9a4w67xFmNqxPSpvypNt0dGvq/Ic/YZ6l9nyBLKKMPteA1exPk95cZzefHjvnacd7mJd0vB05k2s4BYmIC8I+93hMJhyjLcSvDU1Qtem3sMA4KDUrkpHruGqzhmIZpmvNDlLP2x5Qb1nHP3UBhFPPzmV4egnx4iOfkU9PUAhsgjgi/749YD0z3b1+s31zfvz6/dnr9/88sPl9fn7N+9c8tWP6ei7f//+3Zurf/nh4ufzXy/v/vHdv/jj5dVdlHjzzd/97//7zc3lT9fnVx//66Nnf3/xyzefP+b+rnlzfXfbvL589/rDfZLhTuiH6/f36315d/V/XPnbP9iL3jnF41+N4oNTPBym+PmPv55fv76L5hW1d8AUSO1+mNovU1GO1n/49tMv/+6bH64+XLx9d3n90Tauzu+M5O6/hfLdq+/v/vurv//09Tev/iZ++g/f3xnRzd/e/dNf7zT69HzpLcYextY+Zmge/ka+d0yfRd/pe/lxKaClt9Jytzv1v12K+Pnyp5/P3l+8/vns5vXlxd0+nr09f/2nnZD4cQHut+3y4pNSz/YwKKZ+9qJl6eQN9b8EPY2R+5LHEXP3vV1/Kev/9hh7jPYKPJuhCLy7HtfgY61vGRtnuBITdmJXQRy5a/G4XcPukPWW/XD+/u5P/huzU5XeqcjsVBPeTNtuYZd/gC0GPh6Ijzf/UznBNfkXf470jum4M/L6zdu3F+/O3l6dv79gLqPnoyBW0Vl62l5LWBBnoY39ct+8vbi7Vn558+OHqwtrkUNk7RvgFdpV+ME09uejYonL2rLawgprhbtlXZpIqPByJniUfGjqFd9u18m00NVrCZKONx08nAHgrbIr5GOmET/PJH6yjAcjf33+g20ZES4/brjiUb+jkdWOiR2X3e1h3qT9xpmwAptEng0Yr9wl8CjMnn7eSGHEyHLenHaTy5cXTByk5rMh8M/L9rjpQ7ruCvjTkuhs7n2K3PduM1lJtz7sg7PoshPjslNhXTYy1Key97TNnN+0GDFqISJOijCbtf2cBAEOCTJwJWctJnQYpdS7JSCa0qFgQBgm5EcPonIWA02bKqZocabdOirJsvVq+iBI+FbJqt/aoCzCIM/WhsDCxaMFabwr91NHy8bFR+laDNqtWJJy9mzFpaeX3bvAui0Ezsd6LQTE1zifgohksZ42sFV0U1Bup25SyG8qWgMZ8QMAmRrF6wnx0TVJzwHznVKz9E4B+IpqkUJ/W0s1FwktZ2Piflu/Lr3wC/PAr0N5WpjqGnV49GWBLGgLZFbJfP60qIURnQkjmlqOqFCKO4vHFpNeSG9igqFa5fyHLURP5mOf2jVfgiShGhvrAU3nfdOiMROm0YMUjQ0tGutRCaHMVFqXAjt7DbJUOL2vRP3HqJv2It5UYQOMoFfSCBCe796kuukRm/YVy6ZdLVBgGzXkmzYgKYqxKWXMI7ZMrWI+HwoCPh/NR9+ICtLkiBouCy1ZQIbqd6/+18XV1Zs/v/r9J3FrkFDSQUI379/ceaI/fnh3ff76gkQHGS4t/gYbtBZx99W7XyQcTbR+kiQbSlSn0J612IiLTYTYgIvNhNgNF1twsR9LrajYSojtuNhGiG242E6IrbjYQYgtuNgdCmQtNxNyCTsLhJ0FwtACYWiBsLRAWFogTC0QphYIWyNMLRC2RphaIGyNMLVA2BphaoGwNcLUImFqhKVFwtIYh8Z4NEIsY2eEWNzMCCOLuJExuuI2xiwsbmLMKcAtjDmyuIER9pVw+yIug4SbF3FzJdy6iGs24cZF+ISE2xbjwRJhXIR1Jdy6mPgg4ebFhDMJty8m+kq4gTHBYsYtjAltM25iTCCecRtjng0ZNzLmkZNxK2OeZBm3MuoBSfgwwsoybmXM4zzjVhYJK8u4lUXCygpuZZGwshJ4sQgZBG5lsdli6xwWshTbCbH5WbV3VZl52riwgIlMcubxc7fHChayz6RZgr57VGf7ziZWaZBCcapQxxUKS4UGLCz0lbAd7gPcvW22ezvox6zi0WeLtYN6zGS0qYyEr1FdrhF7wsP0hFfohIfpCa/4CQ/LE16hEx6mJ7ziJzwsT3jFT/jygDf2gIfpAW/QAZ+ezQad76mJNPx4L093Y0/39HA36HBPz3bDz/byaDfs8p6qQ5zspTrwwV6K6uS5np7qjpzq6aZ15FBP17nDZ3q565080tMT3ZETPTX4Dh/opbF25DxPr44OH+fl1drx07w8zoM8zvNbeiDnee5hB3Kg5z5xwCd67aMHeaTnIchAzvQ8whrwoV4HWAM51fOgccDHeh0TD/hcr4P1sJEHex5fhw052fPHSNiguzrNZcBne/3CCht5uOcvyLAV/lnuSKq8JGe9GvOsHbaMzrxEHRmDeVzZMnYFbuCR58gIzCPIkRGZd4sjIxHBuCMiE08CRwQTNTsiKhHpOiIaXUgEaMsCUVLecIoJoqJMEFfEDQ9j7UWMRCjtSIh4wOlISHiU6EjIRPTjiChEDOaIqESY4ohgQgtHRCdcriNiEB7XFpH4FDZinPse/bdXl+9tzR6X+LuCwH4i7zRtqpbEZ6whdhoGq1dNcg9bLgEgegxhILnY1V4feUJMGcTd/liwgXTrPPgTkjuYWMf+ZqK2+iQJ0S0HHkIKyY08hhSSm3gQKSQ3M7Gks0eFR6JCulUeigrJxXLlU1vMAp4V0k0AtCJyy8bE6vY3l8CDYiHdIg+KheQmHhQLyc08KBaSC70gxnSLKo+rhVRrNK4WEttpXC0kdtC4WkRshWyozPanBhqaC2kWaWguJBZ6WOfpB2ca3QtpVmh0LyQWitLC9IOZapUjotMYY+jjBp0aQMSyxdnp6rXAZgS6LSey+QroUxMLUYakZhaiDEktLEQZklpZiDIktbEQZUhqZyHKkNTBSm1Ix8bGAp8RXXtggc+QVBqkDa0ADdKGpPIgbWgJeJA2JLbS2kKL0GhtIbGdhpRDizBoSDkidmy0tsgijEBrC4mNNAAeWoREA+AhsZnWFlqEQmsLia00XB9ahEbD9SGxndYWWoRBa9uQXr6Nbi7oiNhANxdAYvlWCGgR+FYISCzfCgEtAt8KAYmttLbQIjRaW0hspxs3oEUYdOMGIpboTo+4L4tEc3pMhNhIt5lAYhPdZgKJzXTlCxLL41kQLvodYsDkDDESBu0zGfYTE9Yf7/7M+btf7n740/n/ufulzaWPZTYKTrMeOgOoqWtypRgG2wox5avfQQn+/ObNjxfXZ69/vriZ1ds+KvnD+bt7SlFDXqC3K6jbFSPI4DhmJPv7aQQY9H5MFzSzCxCs83r57s312U93a3D2558vLq7s7y9ix4ejOW1pIVmaP55z82+gpJthumWd/fAw/fDBfvgwvvvZpWdSILBdHm2mdqJNrYgHLYGGlmabllg7S9OP583M+HiT5M/4YxBICL/DIajETt+lvEYuhmW3y0kzqUOnoEwPwZB6HuwzkDccgmars8NIoD0P+TtHmYij2RxlEt3z4CqT+Z4HV1bReh4caZWA7Dnr1AjIniOi8z0P7voMyCzC1C7KpjVO2CqVQCASHYUi3zjhrVBJBLrRUSfzjROuOkXrdXCkVb4Nw9WsERhOZ6E6geF0RAy+acL7oheN27ZxxKlx1KA1Xjgq8bwFyEuvJvr563xtpuGkkH6FaYdItmoYeKB8nju01KoxoC1Hq840WACDMGLF2k+2mVZtI/otMhDkYT3deapTJMAfjoiEh67QV2W8EQHZOqizOxPyKh5jOitGNMM6EjoRMzkiBhEz2SKIAn4gFpio4D9eLBB9XaSLSpDYRERWzjpmIhpyRBQiTnBEVCJOcEQ0gZ4XORGdlxsRuUPg/UU4DT8bBkD6/njans+NgqZqBoFiGPmCKHAMI3ITxSFjnrJd5R1Z3WxrB61uQVPhIX3ew/tUuOnaRhUYiwGXOZrAWAzYx64Oj6x0XJxjf/zvrjKP/KEgG0zaNiaQC/b02cBoOxbnz12VtCvVr47f9uz4ZVNeYvRu6m4momh/1uG7IxFF+7NGiK3MqtQTjl7jKaoR/TtPUY2IpUwyyYclCFTYCFWvQIWNiIVNMj+zyGCKSwRwPq49QdpV8ldXxmRwa2Io5SOxepVn/EbENolSyvl0uusfUpHu+kekEjTyAY6KE8Ei73h8U2pkEf+Q1MQi/iGpmUX8Q1ILi/iHpFYWmw9JbSzeHZLaWbw7JHXQCHJELEFc4AWkpthAY7IhsZHGZENiE53igcRmOsUDiS10isd2AgxzPGFVDHM8YVYMczxhVwRzfCTsimA3iIRdEeQGkbArgtsgEnZFURv0p6HqK9edMy12Q8QWulkdmAqZMp8OMflWUubzH5B+nRZrJwwyi9B8+FpbWtnofnfkaxmmg4FLjWwuAJKa2FQAJDXjnGrO1hS8W96RUNkkAfRljc0RQFI7+8aGpA42IYBIxXgMZkbM0BhEXK/IPokhqYl8xEJCM/nahIQW8lkICa0wG56z1418AkNasQO0IKHs/CxEaGPHZ0FC2elZkFAEv1AnO03QG+DuiWA3wD0phGMYs0/lh6cC5frU+KIvkoXaQRxAOPdnlZ8QzPsRyvfThI2/M7Q5C2YFPO0gERQCn9W6Bwk67ygdNeQ8rXQi/07U/gzbCFC1P0OCRvNsB9hGm6GpTCbKQ5jpzIMyCmD3FCbjk1hgNFUaGx7bmyjRBHHzP5izI4Fqmsm2jITHsY4WGQ6OHAEF9rmOgAp7MkcA1jP2cHydheSPb0POGX9861ps3jYajwO0HeUtEPDdhujJ0xpCeiaahRESmwmcMPT5hc4YQXryiTJILE1wCEntdHoKEjvwCxzZKgZ30GAtGdhBx6VG3PVA357YdBCkZWZTV5BUmvUQklrZJBMkteHOGdqnziaHIC0Hm8hCpNJYBEhoIPNYkNAINxiYhI2ZAB3gJ5LAHBCbUpTmWuerWRpESMFG5sQgoR0OUJ1PZfkOEa3omfSQ0ACH0vanErgBPARIbFINEpqlx6vz2TSZIaQizWUISW14s4/ztZ0Fh0B6DRbJgkiFWvjD7KLKdP8QpBfNTghJJSkwHtAmzodnFrUCqUiDaiCpFe95cr6WBtNAetFEMp9Wctg68tkAYPhNphAAn8R2ROyUPib4mYHh5iHDN+YfivQfitofSuwf6trfyVoRwT41OxQBqLW4DVUqIjhKN3pPNaU/G+jV+Q9exO3oOOa/rZPf7gAE5m/H7Ldh/tun023+OC5+XGY/Tosf99mPeTRZAFBqufKzc8KGyGXICDBNmQnzmMQO0p1tj99tSiGSYZBajRjzgQkMBN4GEoi/2u2hqLklaOnzVIY0odsThhdSPAl4JcWToJUEPWkEL4EnYuDxvyOiYxQzYbq0bIX9IQ70pEU83PVEJLpZPQAtY7kLtzwkt9AlK0xupVveMbmNLl1hcjtdu8LkDh6djMgdG11swuTS+GJMbKTLTZhcGmKMiaV7ozGxha3oYGJpNDImtrE1HUxsZ4s6mFh6hhQitjAl/UiIDcj7KJiYnrJF5IHk/TghLyTvxxl6Inm/LtAbyft1hR5JH3/9h2+/uXx/8cvdv/zh6sPF23eX1x+36+5XF1d3/+2/f/jl7avfvz9/9/7V9+c3H4OEXy/e3XzqAuotxh7G1j5u4uX1jxcfQ81sCTz74c2bP+2k/o/Lq/MPN//pXuTZf7s++93Z95/+wfndOfj14l8ehG3OX7v9d08VDP8='! ! !FactorioTests class methodsFor: 'as yet unclassified' stamp: 'raa 6/27/2020 11:24'! string2 " 0 removed from front " ^'eNp9kNsKwjAMht8l1y04T8O+ioh0Nczgmo2mimPs3c02EEH0JpDT//3JAFVzxy4RZ3ADUGhZwB0HEKrZN1Mt9x2CA8oYwQD7OGVeBGPVENc2+nAlRlvAaID4gk9wxXgygJwpEy56c9Kf+R4rTDrwX8lA14outzw5UMGVgV6jEhIGmg01bU2SKVgJhBzQdj7c1MMXa/1mEQumrLUf+natgAspYmltpzPmw93Hnww8MMk8UJabothvDrvddhxfi0NvAQ=='! ! !FactorioTests class methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 20:09'! string4 " 0 in front ok now (self decode: self string4) bobEdit " ^'0eNqNkEsOwjAMRO/idUC0fEpzFYRQCgYspU6VuhVVlbvjlA0rxNLWzPOMZ2j8gF0kFrAz0DVwD/Y0Q08Pdj7vZOoQLJBgCwbYtXnKOnEsq2toG2InIUIyQHzDF9ginQ0gCwnhB7cM04WHtsGogp8gA13o1Rs431feqix3672BSZ11fVzv9ZQaJQZ/afDpRlKXSu/kBeNfBaIjDwtmyNWL7+wpx1/k9us9BkZlL6HKY7Gr6rLa1ptDUR1SegOStWzh'! ! !FactorioTests class methodsFor: 'as yet unclassified' stamp: 'raa 4/22/2021 13:29'! test1 " self test1 self decode: self string4 self decode: self myOwnString " | f original new aStream z zipped encoded enc o2 | original _ self decode: self string1. f _ FileStream fileNamed: 'test1.json'. f nextPutAll: original. f close. new _ self replaceOldIn: original. f _ FileStream fileNamed: 'test2.json'. f nextPutAll: new. f close. aStream _ WriteStream on: ByteArray new. z := ZLibWriteStream on: aStream. z nextPutAll: new. z close. zipped _ aStream contents. encoded _ Base64MimeConverter mimeEncode: zipped readStream. {aStream. z. zipped. encoded} explore. enc _ encoded contents. FileDirectory default deleteFileNamed: 'test2.blueprint'. f _ FileStream fileNamed: 'test2.blueprint'. f nextPutAll: '0',enc. f close. o2 _ self decode: enc. f _ FileStream fileNamed: 'test3.json'. f nextPutAll: o2. f close. ! ! !FactorioTests class methodsFor: 'as yet unclassified' stamp: 'raa 6/27/2020 13:29'! testClipboard " self testClipboard self decode: self string2 " | original new aStream z zipped encoded enc | original _ self decode: Clipboard clipboardText asString halt. new _ self replaceOldIn: original. aStream _ WriteStream on: ByteArray new. z := ZLibWriteStream on: aStream. z nextPutAll: new. z close. zipped _ aStream contents. encoded _ Base64MimeConverter mimeEncode: zipped readStream. {aStream. z. zipped. encoded} explore. enc _ encoded contents. Clipboard clipboardText: '0',enc. ! ! FactorioMapEntity subclass: #FactorioTransport instanceVariableNames: 'materials' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioTransport methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 07:33'! addMaterial: x materials add: x! ! !FactorioTransport methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 07:28'! initialize materials _ Set new. super initialize! ! !FactorioTransport methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 20:19'! isTransport ^true! ! !FactorioTransport methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 07:33'! materials ^materials! ! !FactorioTransport methodsFor: 'as yet unclassified' stamp: 'raa 4/27/2021 07:40'! printOn: s super printOn: s. s nextPutAll: ' ['. materials do: [ :e | s nextPutAll: e] separatedBy: [s nextPutAll: ' & ']. s nextPutAll: ']' ! ! FactorioTransport subclass: #FactorioBelt instanceVariableNames: 'purpose' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 14:54'! directionTo: nextBelt nextBelt ifNil: [^direction _ 0]. self x = nextBelt x ifTrue: [ ^direction _ self y > nextBelt y ifTrue: [0] ifFalse: [4] ]. ^direction _ self x > nextBelt x ifTrue: [6] ifFalse: [2] ! ! !FactorioBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:39'! entityGraphicName | offset | direction = 2 ifTrue: [offset _ 0]. direction = 6 ifTrue: [offset _ 64]. direction = 0 ifTrue: [offset _ 128]. direction = 4 ifTrue: [offset _ 192]. ^{'entity/express-transport-belt/express-transport-belt.png'. 15@(19+offset) extent: 34@34}! ! !FactorioBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 11:57'! initialize self extent: 1@1. super initialize. ! ! !FactorioBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/28/2021 11:40'! printOn: s super printOn: s. s nextPutAll: ' p=',purpose asString ! ! !FactorioBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:24'! showBorder ^false! ! !FactorioBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 17:47'! simpleName ^'express-transport-belt'! ! !FactorioBelt methodsFor: 'accessing' stamp: 'raa 4/28/2021 11:38'! purpose ^purpose! ! !FactorioBelt methodsFor: 'accessing' stamp: 'raa 4/28/2021 11:38'! purpose: x purpose _ x! ! FactorioTransport subclass: #FactorioPipe instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioPipe methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 20:41'! blueprintOn: s super blueprintOn: s. ! ! !FactorioPipe methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 13:46'! entityGraphic | f | f _ Form extent: 32@32 depth: 32. f fillColor: Color veryLightGray. ^f! ! !FactorioPipe methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 11:57'! initialize self extent: 1@1. super initialize. ! ! !FactorioPipe methodsFor: 'as yet unclassified' stamp: 'raa 4/24/2021 18:21'! printOn: s super printOn: s. ! ! !FactorioPipe methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:24'! showBorder ^false! ! !FactorioPipe methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 13:45'! simpleName ^'pipe'! ! FactorioTransport subclass: #FactorioPipeToGround instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioPipeToGround methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 20:41'! blueprintOn: s super blueprintOn: s. ! ! !FactorioPipeToGround methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 14:01'! entityGraphic | f | f _ Form extent: 32@32 depth: 32. f fillColor: Color lightGray. ^f! ! !FactorioPipeToGround methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 11:57'! initialize self extent: 1@1. super initialize. ! ! !FactorioPipeToGround methodsFor: 'as yet unclassified' stamp: 'raa 4/24/2021 18:21'! printOn: s super printOn: s. ! ! !FactorioPipeToGround methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:24'! showBorder ^false! ! !FactorioPipeToGround methodsFor: 'as yet unclassified' stamp: 'raa 4/25/2021 14:01'! simpleName ^'pipe-to-ground'! ! FactorioTransport subclass: #FactorioSplitter instanceVariableNames: 'purpose' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioSplitter methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 13:46'! direction: d direction _ d. d \\ 4 = 0 ifFalse: [^self extent: 1@2]. self extent: 2@1.! ! !FactorioSplitter methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 13:46'! initialize self extent: 1@2. super initialize. ! ! !FactorioSplitter methodsFor: 'as yet unclassified' stamp: 'raa 5/30/2021 13:40'! simpleName ^'express-splitter'! ! FactorioTransport subclass: #FactorioUndergroundBelt instanceVariableNames: 'inputOrOutput' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioUndergroundBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 20:39'! blueprintOn: s super blueprintOn: s. s comma; key: 'type' andValue: inputOrOutput . ! ! !FactorioUndergroundBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/24/2021 20:29'! entityGraphicName | offset top | inputOrOutput = 'output' ifTrue: [top _ 27]. inputOrOutput = 'input' ifTrue: [top _ 123]. direction = 0 ifTrue: [offset _ 32]. direction = 2 ifTrue: [offset _ 130]. direction = 4 ifTrue: [offset _ 224]. direction = 6 ifTrue: [offset _ 320]. ^{'entity/express-underground-belt/express-underground-belt-structure.png'. offset@(top) extent: 40@40} ! ! !FactorioUndergroundBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/20/2021 11:57'! initialize self extent: 1@1. super initialize. ! ! !FactorioUndergroundBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/24/2021 20:09'! printOn: s super printOn: s. s nextPutAll: ' dir=',direction asString,' in/out=',inputOrOutput asString ! ! !FactorioUndergroundBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/23/2021 19:24'! showBorder ^false! ! !FactorioUndergroundBelt methodsFor: 'as yet unclassified' stamp: 'raa 4/24/2021 18:22'! simpleName ^'express-underground-belt'! ! !FactorioUndergroundBelt methodsFor: 'accessing' stamp: 'raa 4/24/2021 18:24'! inputOrOutput ^ inputOrOutput! ! !FactorioUndergroundBelt methodsFor: 'accessing' stamp: 'raa 4/24/2021 18:24'! inputOrOutput: anObject inputOrOutput := anObject.! ! FactorioChest subclass: #FactorioWoodenChest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Bob-Factorio'! !FactorioWoodenChest methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 19:44'! initialize self extent: 1@1. super initialize. ! ! !FactorioWoodenChest methodsFor: 'as yet unclassified' stamp: 'raa 4/30/2021 19:44'! simpleName ^'wooden-chest'! !