oolite 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a7c6827b7924ebbeca80991a4ce8557f68796687
4
+ data.tar.gz: 1910d5718f908f7f3726c6307c4060fb808916d7
5
+ SHA512:
6
+ metadata.gz: 8cf6dc36233b7896e6ae727a9190049d260c528249798d39ff242a21c95fa34fb627ed056d8927f9f6d108e031dac6ff3c294cf93db8d759bd9afbd569624e6e
7
+ data.tar.gz: ded361ce7720a382df492177d6c1bb55a6d1709ea7d31ed332682ef95529dc99e7452602713aee9413ddba63bc3610b12cf078e686f4b87d0df47bcb02d8cd86
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in oolite.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Jeff McAffee
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # Oolite
2
+
3
+ Utility scripts for monitoring the Oolite game's system markets and
4
+ providing trading suggestions based on the market data.
5
+
6
+ ## Installation
7
+
8
+ If you wish to clone the repository:
9
+
10
+ $ git clone https://github.com/jmcaffee/oolite.git
11
+ $ cd oolite
12
+ $ bundle
13
+
14
+ # Use rake to install the gem
15
+ rake install
16
+
17
+ # To start a pry console using the local version of the gem
18
+ rake console
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install oolite
23
+
24
+ ## Usage
25
+
26
+ These scripts are provided as standalone utilities. Simply install
27
+ the gem as detailed above to provide the following commands:
28
+
29
+ ooconfig - configure the scripts (ie. your save file location)
30
+ ootrader - suggest the best trades based on current location and destination
31
+ ooupdatemarket - update the market data (pulls from your save file)
32
+
33
+ ### ooconfig
34
+
35
+ `ooconfig` displays and edits the configuration file.
36
+
37
+ The oolite scripts will look for the config file starting in your current directory
38
+ and ascending the directory tree until it reaches the root, then look in your _home_
39
+ directory. I would suggest running your initial configuration from your home dir
40
+ so the scripts can always find the config file no matter where you call them from.
41
+
42
+ To get started, determine where your save file is located at and change to your
43
+ home directory.
44
+
45
+ # Assuming your save file is at ~/oolite-saves/Jameson.oolite-save
46
+ cd ~
47
+ ooconfig save_file_path ~/oolite-saves/Jameson.oolite-save
48
+
49
+ You can see where the config file is located at by running
50
+
51
+ ooconfig path
52
+
53
+ To see the current configuration values, run
54
+
55
+ ooconfig show
56
+
57
+ To see available commands/options, run
58
+
59
+ ooconfig
60
+
61
+ ### ooupdatemarket
62
+
63
+ `ooupdatemarket` saves its data in the same directory as your game save file.
64
+ It also generates a `CSV` dump of the most recent market data for all visited
65
+ systems.
66
+
67
+ Note that the data is only current for the system you are located at when you
68
+ save your game. The other system data is retained from the last time you visited
69
+ the system, saved your game, and ran `ooupdatemarket`.
70
+
71
+ ### ootrader
72
+
73
+ `ootrader` will ask you for your destination system based on existing data
74
+ (ie. you've visited the destination at least once, saved your game, and ran
75
+ `ooupdatemarket`), then calculate and display all profitable trades, then
76
+ display its suggested trades to make the most profit.
77
+
78
+ It uses the current market data, your available credits and max cargo space
79
+ to make its suggestions.
80
+
81
+ ## Workflow (Gameflow?)
82
+
83
+ * Dock your ship in game
84
+ * Sell your current cargo
85
+ * Refuel you ship
86
+ * Save your game
87
+
88
+ In a terminal, update your market data
89
+
90
+ ooupdatemarket
91
+
92
+ Now see what it suggests for trades
93
+
94
+ ootrader
95
+
96
+ **Note:** you'll need to visit more than one system, save your game,
97
+ and run `ooupdatemarket` before you'll be able to choose any destinations.
98
+
99
+ * In game, make your trades/purchases
100
+ * Travel to your destination
101
+ * Profit
102
+
103
+ ## About This Project
104
+
105
+ These scripts were thrown together over the course of a couple of days.
106
+ As such, the code is not the prettiest, and I'm sure it's ripe for
107
+ refactoring.
108
+
109
+ I know that there are OXPs that can perform the same functionality (and
110
+ they're in-game too!) but I'm just scratching my itch to code here.
111
+
112
+ If you find errors, please create issues in the issue tracker. If you'd like
113
+ to add additional features, fork the project and submit a pull request.
114
+
115
+ ## Roadmap
116
+
117
+ * Refactor the code
118
+
119
+ ## Contributing
120
+
121
+ 1. Fork it ( https://github.com/[my-github-username]/oolite/fork )
122
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
123
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
124
+ 4. Push to the branch (`git push origin my-new-feature`)
125
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc 'start a console'
4
+ task :console do
5
+ require 'pry'
6
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
7
+ require 'oolite'
8
+ ARGV.clear
9
+
10
+ def console_help
11
+ puts <<CONSOLE_HELP
12
+ CONSOLE HELP
13
+ -------------
14
+
15
+ Starts a pry console and requires the *local* version of Oolite.
16
+
17
+ Search and load the Oolite configuration (defaults will be set if
18
+ no configuration file is found)
19
+
20
+ Oolite.configure
21
+
22
+ OR
23
+
24
+ Oolite.configure do |config|
25
+ config.some_setting = some_value
26
+ end
27
+
28
+ After a configuration has been instantiated, you can retrieve the
29
+ current configuration with
30
+
31
+ Oolite.configuration
32
+
33
+
34
+ CONSOLE_HELP
35
+ end
36
+
37
+ console_help
38
+ Pry.start
39
+ end
40
+
data/bin/ooconfig ADDED
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # vim: set ft=ruby
4
+ ##############################################################################
5
+ # File:: ooconfig
6
+ # Purpose:: Configure the oolite utility
7
+ #
8
+ # Author:: Jeff McAffee 05/20/2015
9
+ # Copyright:: Copyright (c) 2015, kTech Systems LLC. All rights reserved.
10
+ # Website:: http://ktechsystems.com
11
+ ##############################################################################
12
+
13
+ require 'pathname'
14
+ begin
15
+ require 'oolite'
16
+ rescue LoadError
17
+ $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
18
+ require 'oolite'
19
+ end
20
+
21
+
22
+ class Main
23
+
24
+ private
25
+
26
+ def usage
27
+ puts "Usage: #{File.basename($0)} [ CMD | OPTION VALUE ]"
28
+ puts
29
+ puts " Available commands:"
30
+ puts " show: show current configuration settings"
31
+ puts " path: show location of config file (if found)"
32
+ puts " update CATEGORY VALUES: update a category item"
33
+ puts " categories:"
34
+ puts " systems: accepts (in order) NAME ECONOMY GOVERNMENT TECHLEVEL"
35
+ puts
36
+ puts " Available options:"
37
+ puts " save_file_path: path to Oolite save file (~ is allowed)"
38
+ puts
39
+ end
40
+
41
+ def current_settings
42
+ puts "= Current Configuration Settings ="
43
+ puts
44
+ Oolite.configure do |config|
45
+ vars = config.instance_variables.map { |v| v.to_s.gsub('@','') }
46
+ vars.each do |var|
47
+ puts "#{var}: #{emit_value(config.send(var))}"
48
+ end
49
+ end
50
+ end
51
+
52
+ def emit_value val
53
+ case val
54
+ when Array
55
+ emit_array(val)
56
+ when Hash
57
+ emit_hash(val)
58
+ else
59
+ return val
60
+ end
61
+ end
62
+
63
+ def emit_array val
64
+ nl = "\n"
65
+ indent = " "
66
+ output = nl.dup
67
+
68
+ val.each do |elem|
69
+ output << indent << elem << nl
70
+ end
71
+ output << nl
72
+ end
73
+
74
+ def emit_hash val
75
+ nl = "\n"
76
+ indent = " "
77
+ output = nl.dup
78
+
79
+ val.each do |key,elem|
80
+ output << indent << "#{key}: " << indent << emit_value(elem) << nl
81
+ end
82
+ output
83
+ end
84
+
85
+ def set_config_values args
86
+ Oolite.configure do |config|
87
+ while args.count > 0
88
+ var = args.shift
89
+ val = args.shift
90
+ config.send("#{var}=", val)
91
+ end
92
+ end
93
+
94
+ Oolite.save_configuration
95
+ end
96
+
97
+ def system_data args
98
+ econ = args.shift
99
+ gov = args.shift
100
+ tech = args.shift
101
+
102
+ data = Hash.new
103
+ data[:economy] = econ unless econ.nil?
104
+ data[:government] = gov unless gov.nil?
105
+ data[:tech_level] = tech unless tech.nil?
106
+
107
+ data
108
+ end
109
+
110
+ def update args
111
+ category = args.shift
112
+ case category
113
+ when "systems"
114
+ Oolite.configure do |config|
115
+ name = args.shift
116
+ val = config.systems.fetch(name, Hash.new)
117
+ #val = val.merge( { name => system_data(args) } )
118
+ val = val.merge( system_data(args) )
119
+ config.systems[name] = val
120
+ end
121
+
122
+ Oolite.save_configuration
123
+ else
124
+ puts "Unknown category: #{category}"
125
+ end
126
+ end
127
+
128
+ def process_cmd args
129
+ return false unless args.count > 0
130
+
131
+ if args.first.downcase == 'show'
132
+ current_settings
133
+ return true
134
+ end
135
+
136
+ if args.first.downcase == 'path'
137
+ path = Oolite.find_config_path
138
+ path = 'Not found' if path.nil? || path.to_s.empty?
139
+ puts "path: #{path}"
140
+ return true
141
+ end
142
+
143
+ if args.first.downcase == 'update'
144
+ args.shift
145
+ update args
146
+ return true
147
+ end
148
+ end
149
+
150
+ public
151
+
152
+ def execute args
153
+ if process_cmd(args)
154
+ return 0
155
+ end
156
+
157
+ if args.count < 2
158
+
159
+ usage
160
+ return 1
161
+ end
162
+
163
+ set_config_values args
164
+ end
165
+ end
166
+
167
+ Main.new.execute ARGV
168
+
data/bin/ootrader ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # vim: set ft=ruby
4
+ ##############################################################################
5
+ # File:: ootrader
6
+ # Purpose:: Determine the best items to buy based on the current location
7
+ # and the next destination
8
+ #
9
+ # Author:: Jeff McAffee 05/20/2015
10
+ # Copyright:: Copyright (c) 2015, kTech Systems LLC. All rights reserved.
11
+ # Website:: http://ktechsystems.com
12
+ ##############################################################################
13
+
14
+ require 'pathname'
15
+ begin
16
+ require 'oolite'
17
+ rescue LoadError
18
+ $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
19
+ require 'oolite'
20
+ end
21
+
22
+
23
+ class Main
24
+
25
+ def execute
26
+ savefile = Oolite.configuration.save_file_path
27
+ if savefile.nil? || savefile.empty?
28
+ fail "save file path not set.\nUse 'ooconfig save_file_path YOUR_PATH' to set."
29
+ end
30
+
31
+ # Update the market file so we're working with the latest data.
32
+ market = Oolite::Market.new
33
+ market.update
34
+
35
+ # Display the best trades.
36
+ trader = Oolite::Trade.new.display
37
+ end
38
+ end
39
+
40
+ Main.new.execute
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # vim: set ft=ruby
4
+ ##############################################################################
5
+ # File:: ooupdatemarket
6
+ # Purpose:: Parse the Oolite save file and generate market info based on the
7
+ # current docked location's market
8
+ #
9
+ # Author:: Jeff McAffee 05/19/2015
10
+ # Copyright:: Copyright (c) 2015, kTech Systems LLC. All rights reserved.
11
+ # Website:: http://ktechsystems.com
12
+ ##############################################################################
13
+
14
+ require 'pathname'
15
+ begin
16
+ require 'oolite'
17
+ rescue LoadError
18
+ $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
19
+ require 'oolite'
20
+ end
21
+
22
+
23
+ class Main
24
+
25
+ def execute
26
+ savefile = Oolite.configuration.save_file_path
27
+ if savefile.nil? || savefile.empty?
28
+ fail "save file path not set.\nUse 'ooconfig save_file_path YOUR_PATH' to set."
29
+ end
30
+
31
+ market = Oolite::Market.new
32
+ market.update
33
+ market.write_csv
34
+ end
35
+ end
36
+
37
+ if ARGV.count > 0 && ARGV.first.downcase == 'upgrade'
38
+ market = Oolite::Market.new
39
+ market.upgrade
40
+ elsif ARGV.count > 0 && ARGV.first.downcase == 'upgradeprice'
41
+ market = Oolite::Market.new
42
+ market.upgrade_price
43
+ else
44
+ Main.new.execute
45
+ end
data/lib/oolite.rb ADDED
@@ -0,0 +1,191 @@
1
+ require "oolite/version"
2
+
3
+ if ENV['DEBUG'].nil?
4
+ $debug = false
5
+ else
6
+ $debug = true
7
+ end
8
+
9
+ module Oolite
10
+ CONFIG_FILE_NAME = '.oolite'
11
+
12
+ class << self
13
+ attr_accessor :configuration
14
+ end
15
+
16
+ ##
17
+ # Setup oolite configuration
18
+ #
19
+ # Attempts to find and load a configuration file the first time
20
+ # it's requested. If a config file cannot be found in the current
21
+ # directory tree (moving towards trunk, not the leaves), the user's
22
+ # home directory will be searched. If still not found, a default
23
+ # configuration object is created.
24
+ #
25
+ # If a block is provided, the configuration object is yielded to the block
26
+ # after the configuration is loaded/created.
27
+ #
28
+
29
+ def self.configure
30
+ if self.configuration.nil?
31
+ unless self.load_configuration
32
+ self.configuration = Configuration.new
33
+ end
34
+ end
35
+ yield(configuration) if block_given?
36
+ end
37
+
38
+ ##
39
+ # Walk up the directory tree from current working dir (pwd) till a file
40
+ # named .oolite is found
41
+ #
42
+ # Returns file path if found, nil if not.
43
+ #
44
+
45
+ def self.find_config_path
46
+ path = Pathname(Pathname.pwd).ascend{|d| h=d+CONFIG_FILE_NAME; break h if h.file?}
47
+ if path.nil? && (Pathname(ENV['HOME']) + CONFIG_FILE_NAME).exist?
48
+ path = Pathname(ENV['HOME']) + CONFIG_FILE_NAME
49
+ end
50
+ path
51
+ end
52
+
53
+ ##
54
+ # Write configuration to disk
55
+ #
56
+ # Writes to current working dir (pwd) if path is nil
57
+ #
58
+ # Returns path of emitted file
59
+ #
60
+
61
+ def self.save_configuration(path = nil)
62
+ # If no path provided, see if we can find one in the dir tree.
63
+ if path.nil?
64
+ path = find_config_path
65
+ end
66
+
67
+ # Still no path? Use the current working dir.
68
+ if path.nil?
69
+ path = Pathname.pwd
70
+ end
71
+
72
+ unless path.to_s.end_with?('/' + CONFIG_FILE_NAME)
73
+ path = Pathname(path) + CONFIG_FILE_NAME
74
+ end
75
+
76
+ path = Pathname(path).expand_path
77
+ File.write(path, YAML.dump(configuration))
78
+
79
+ path
80
+ end
81
+
82
+ ##
83
+ # Load the configuration from disk
84
+ #
85
+ # Returns true if config file found and loaded, false otherwise.
86
+ #
87
+
88
+ def self.load_configuration(path = nil)
89
+ # If no path provided, see if we can find one in the dir tree.
90
+ if path.nil?
91
+ path = find_config_path
92
+ end
93
+
94
+ return false if path.nil?
95
+ return false unless Pathname(path).exist?
96
+
97
+ File.open(path, 'r') do |f|
98
+ self.configuration = YAML.load(f)
99
+ puts "configuration loaded from #{path}" if $debug
100
+ end
101
+
102
+ true
103
+ end
104
+
105
+ class Configuration
106
+ attr_accessor :save_file_path
107
+ attr_accessor :market_data_filename
108
+ attr_accessor :current_system_name
109
+ attr_accessor :current_credits
110
+ attr_accessor :cargo
111
+ attr_accessor :trade_contraband
112
+ attr_accessor :contraband
113
+ attr_accessor :economies
114
+ attr_accessor :governments
115
+ attr_accessor :systems
116
+
117
+
118
+ def initialize
119
+ reset
120
+ end
121
+
122
+ def reset
123
+ @save_file_path = ''
124
+
125
+ @market_data_filename = 'oolite.market'
126
+
127
+ @current_system_name = ''
128
+ @current_credits = 0
129
+ @cargo = 0
130
+
131
+ @trade_contraband = false
132
+
133
+ @contraband = [
134
+ "Slaves",
135
+ "Narcotics",
136
+ "Firearms",
137
+ ]
138
+
139
+ @economies = [
140
+ "Rich Industrial",
141
+ "Average Industrial",
142
+ "Poor Industrial",
143
+ "Mainly Industrial",
144
+ "Mainly Agricultural",
145
+ "Rich Agricultural",
146
+ "Average Agricultural",
147
+ "Poor Agricultural"
148
+ ]
149
+
150
+ @governments = [
151
+ "Anarchy",
152
+ "Feudal",
153
+ "Multi-Government",
154
+ "Dictatorship",
155
+ "Communist",
156
+ "Confederacy",
157
+ "Democracy",
158
+ "Corporate State"
159
+ ]
160
+
161
+ @systems = Hash.new
162
+ end
163
+
164
+ ##
165
+ # Control which instance vars are emitted when dumped to YAML.
166
+ # Example - not used at this time.
167
+ #
168
+
169
+ def encode_with(coder)
170
+ vars = instance_variables.map { |x| x.to_s }
171
+ vars = vars - ["@dummy"]
172
+
173
+ vars.each do |var|
174
+ var_val = eval(var)
175
+ coder[var.gsub('@', '')] = var_val
176
+ end
177
+ end
178
+ end # Configuration
179
+ end
180
+
181
+ require 'yaml'
182
+
183
+ # Initialize the configuration
184
+ Oolite.configure
185
+
186
+ require 'oolite/csv_doc'
187
+ require 'oolite/save_file'
188
+ require 'oolite/market_file'
189
+ require 'oolite/market'
190
+ require 'oolite/trade'
191
+
@@ -0,0 +1,68 @@
1
+ ##############################################################################
2
+ # File:: csv_doc.rb
3
+ # Purpose:: CSVDoc class for emitting formatted CSV documents
4
+ #
5
+ # Author:: Jeff McAffee 05/19/2015
6
+ # Copyright:: Copyright (c) 2015, kTech Systems LLC. All rights reserved.
7
+ # Website:: http://ktechsystems.com
8
+ ##############################################################################
9
+
10
+
11
+ module Oolite
12
+ class CSVDoc
13
+ def lines
14
+ @lines ||= Array.new
15
+ end
16
+
17
+ def add line = []
18
+ new_line = Array.new
19
+
20
+ Array(line).each do |elem|
21
+ if elem.include?(',')
22
+ # Quote element if it contains a comma
23
+ new_line << "\"#{elem}\""
24
+ else
25
+ new_line << elem
26
+ end
27
+ end
28
+
29
+ lines << new_line
30
+ end
31
+
32
+ def write filepath
33
+ pad_lines
34
+ output_lines = Array.new
35
+ lines.each do |line|
36
+ output_lines << line.join(',')
37
+ end
38
+
39
+ File.open(filepath, 'w') do |f|
40
+ f.write(output_lines.join("\n"))
41
+ end
42
+ end
43
+
44
+ def pad_lines
45
+ max = 0
46
+ lines.each do |line|
47
+ len = line.count
48
+ if len > max
49
+ max = len
50
+ end
51
+ end
52
+
53
+ padded_lines = Array.new
54
+ lines.each do |line|
55
+ if line.count < max
56
+ pads = max = line.count
57
+ pads.times.each do
58
+ line << ''
59
+ end
60
+ end
61
+ padded_lines << line
62
+ end
63
+
64
+ @lines = padded_lines
65
+ end
66
+ end
67
+ end
68
+
@@ -0,0 +1,143 @@
1
+ ##############################################################################
2
+ # File:: market.rb
3
+ # Purpose:: Write out a market data file
4
+ #
5
+ # Author:: Jeff McAffee 05/19/2015
6
+ # Copyright:: Copyright (c) 2015, kTech Systems LLC. All rights reserved.
7
+ # Website:: http://ktechsystems.com
8
+ ##############################################################################
9
+
10
+ require 'pathname'
11
+ require 'yaml'
12
+
13
+ module Oolite
14
+ class Market
15
+ def initialize
16
+ Oolite.configure do |config|
17
+ @save_file_path = Pathname(config.save_file_path)
18
+ path = @save_file_path.dirname
19
+ @market_data_path = path + (Pathname(config.market_data_filename).to_s + '.yml')
20
+ @market_data_csv = path + (Pathname(config.market_data_filename).to_s + '.csv')
21
+ end
22
+ end
23
+
24
+ def update filename = nil
25
+ filename ||= @save_file_path
26
+
27
+ savefile = SaveFile.new filename
28
+
29
+ add_credits_to_config savefile.credits
30
+ add_cargo_to_config savefile.cargo
31
+
32
+ system_name = savefile.current_system_name
33
+ add_system_to_config system_name
34
+ puts "Updating data for #{system_name} system"
35
+
36
+ data[system_name] = savefile.local_market_data
37
+
38
+ write_data_file
39
+ end
40
+
41
+ def upgrade
42
+ mf = MarketFile.new
43
+ old_data = mf.data
44
+
45
+ old_data.each do |key, d|
46
+ updated_hash = Hash.new
47
+ d.each do |item, price|
48
+ if price.class == Hash
49
+ updated_hash[item] = price
50
+ else
51
+ updated_hash[item] = { price: price, amount: 0 }
52
+ end
53
+ end
54
+
55
+ data[key] = updated_hash
56
+ end
57
+
58
+ write_data_file
59
+ end
60
+
61
+ def upgrade_price
62
+ mf = MarketFile.new
63
+ old_data = mf.data
64
+
65
+ old_data.each do |key, d|
66
+ updated_hash = Hash.new
67
+ d.each do |item, info|
68
+ updated_hash[item] = { price: (info[:price] * 10).to_i, amount: info[:amount] }
69
+ end
70
+
71
+ data[key] = updated_hash
72
+ end
73
+
74
+ write_data_file
75
+ end
76
+
77
+ def write_csv filename = nil
78
+ filename ||= @market_data_csv
79
+ csv_doc = CSVDoc.new
80
+ format_data csv_doc
81
+ csv_doc.write filename
82
+
83
+ puts "CSV file written to #{filename}"
84
+ end
85
+
86
+ private
87
+
88
+ def data
89
+ @data ||= MarketFile.new.data
90
+ end
91
+
92
+ def write_data_file
93
+ mf = MarketFile.new
94
+ mf.data = data
95
+ mf.write
96
+ end
97
+
98
+ def format_data csv_doc
99
+ line = Array.new
100
+ line << ''
101
+ line = line + data.keys
102
+ csv_doc.add line
103
+
104
+ data[data.keys.first].keys.each do |type|
105
+ line = Array(type)
106
+ data.each do |location, local_data|
107
+ line << (local_data[type][:price] / 10.0).to_s
108
+ end
109
+ csv_doc.add line
110
+ end
111
+ end
112
+
113
+ def add_system_to_config system
114
+ Oolite.configure do |config|
115
+ config.current_system_name = system
116
+
117
+ unless config.systems.key?(system)
118
+ details = { economy: nil,
119
+ government: nil,
120
+ tech_level: nil,
121
+ }
122
+ config.systems[system] = details
123
+ end
124
+ end
125
+ Oolite.save_configuration
126
+ end
127
+
128
+ def add_credits_to_config credits
129
+ Oolite.configure do |config|
130
+ config.current_credits = credits
131
+ end
132
+ Oolite.save_configuration
133
+ end
134
+
135
+ def add_cargo_to_config cargo
136
+ Oolite.configure do |config|
137
+ config.cargo = cargo
138
+ end
139
+ Oolite.save_configuration
140
+ end
141
+ end
142
+ end
143
+
@@ -0,0 +1,55 @@
1
+ ##############################################################################
2
+ # File:: market_file.rb
3
+ # Purpose:: Store market data
4
+ #
5
+ # Author:: Jeff McAffee 05/20/2015
6
+ # Copyright:: Copyright (c) 2015, kTech Systems LLC. All rights reserved.
7
+ # Website:: http://ktechsystems.com
8
+ ##############################################################################
9
+
10
+ require 'pathname'
11
+ require 'yaml'
12
+
13
+ module Oolite
14
+ class MarketFile
15
+ def initialize
16
+ Oolite.configure do |config|
17
+ @save_file_path = Pathname(config.save_file_path)
18
+ path = @save_file_path.dirname
19
+ @market_data_path = path + (Pathname(config.market_data_filename).to_s + '.yml')
20
+ end
21
+ end
22
+
23
+ def data
24
+ @data ||= self.load
25
+ end
26
+
27
+ def data= new_data
28
+ @data = new_data
29
+ end
30
+
31
+ def systems
32
+ data.keys
33
+ end
34
+
35
+ def load
36
+ if @market_data_path.nil? || @market_data_path.to_s.empty? || !@market_data_path.exist?
37
+ Hash.new
38
+ else
39
+ input = YAML.load_file(@market_data_path)
40
+ input or Hash.new
41
+ end
42
+ rescue
43
+ Hash.new
44
+ end
45
+
46
+ def write
47
+ raise "Missing filename" if @market_data_path.nil? || @market_data_path.to_s.empty?
48
+
49
+ File.open(@market_data_path, 'w') do |f|
50
+ f.write data.to_yaml
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,71 @@
1
+ ##############################################################################
2
+ # File:: save_file.rb
3
+ # Purpose:: Parse the Oolite save file
4
+ #
5
+ # Author:: Jeff McAffee 05/19/2015
6
+ # Copyright:: Copyright (c) 2015, kTech Systems LLC. All rights reserved.
7
+ # Website:: http://ktechsystems.com
8
+ ##############################################################################
9
+
10
+ require 'pathname'
11
+ require 'nokogiri'
12
+
13
+ module Oolite
14
+ class SaveFile
15
+ def initialize filename
16
+ parse filename
17
+ end
18
+
19
+ def parse filename
20
+ raise "Cannot find save file #{filename}" unless Pathname(filename).exist?
21
+
22
+ @doc = Nokogiri::XML(File.open(filename)) do |config|
23
+ config.noblanks
24
+ end
25
+ end
26
+
27
+ def current_system_name
28
+ node = get_data_node_for_key 'current_system_name'
29
+ node.text
30
+ end
31
+
32
+ def cargo
33
+ node = get_data_node_for_key 'max_cargo'
34
+ node.text.to_i
35
+ end
36
+
37
+ def credits
38
+ node = get_data_node_for_key 'credits'
39
+ node.text.to_i
40
+ end
41
+
42
+ def local_market_data
43
+ node = get_data_node_for_key 'localMarket'
44
+
45
+ mdata = {}
46
+
47
+ node.children.each do |child|
48
+ type = child.children[0].text
49
+ amount = child.children[1].text.to_i
50
+ # Price is stored as an integer and displayed as price / 10.
51
+ price = child.children[2].text.to_i
52
+ mdata[type] = { amount: amount, price: price }
53
+ end
54
+
55
+ mdata
56
+ end
57
+
58
+ private
59
+
60
+ def get_data_node_for_key key
61
+ @doc.css('dict').children.each do |node|
62
+ if node.name == 'key' && node.text == key
63
+ return node.next
64
+ end
65
+ end
66
+
67
+ raise "No data found for key #{key}"
68
+ end
69
+ end
70
+ end
71
+
@@ -0,0 +1,213 @@
1
+ ##############################################################################
2
+ # File:: trade.rb
3
+ # Purpose:: Determine the best trades based on current location and the next
4
+ # destination
5
+ #
6
+ # Author:: Jeff McAffee 05/20/2015
7
+ # Copyright:: Copyright (c) 2015, kTech Systems LLC. All rights reserved.
8
+ # Website:: http://ktechsystems.com
9
+ ##############################################################################
10
+
11
+ require 'pathname'
12
+ require 'yaml'
13
+
14
+ module Oolite
15
+ class Trade
16
+ def display
17
+ puts "= Oolite Trader ="
18
+ puts
19
+ puts " Current Location: #{current_system_name}"
20
+ puts
21
+ puts " Available destinations:"
22
+ puts
23
+
24
+ systems = market.systems
25
+ systems.delete current_system_name
26
+ systems.each_with_index do |sys,i|
27
+ puts " #{i} - #{sys}"
28
+ end
29
+ puts
30
+ choice = ask " Choose your destination (q to abort): "
31
+
32
+ return if choice.downcase == 'q'
33
+ puts
34
+
35
+ dest_system = systems[choice.to_i]
36
+
37
+ puts " -- Suggested Trades (best to worst) for #{dest_system} --"
38
+ puts
39
+ puts " #{'Item'.ljust(14)} #{'Amt Avail'.to_s.ljust(10)} #{'PricePerUnit'.to_s.rjust(8)} #{'ProfitPerUnit'.to_s.rjust(12)}"
40
+ puts
41
+
42
+ avail_trades = all_trades(dest_system)
43
+ avail_trades.each do |trade|
44
+ puts " " + trade.to_s
45
+ end
46
+
47
+ puts
48
+
49
+ puts " -- Suggested Transactions for #{dest_system} --"
50
+ puts
51
+ puts " #{'Item'.ljust(14)} #{'Purch Amt'.to_s.ljust(10)} #{'Profit'.to_s.rjust(8)}"
52
+ puts
53
+
54
+ trans, total_profit = calc_best_trades avail_trades
55
+ trans.each do |tran|
56
+ name = tran[:item].name
57
+ revenue = tran[:item].revenue
58
+ amount = tran[:amount]
59
+ profit = revenue * amount
60
+
61
+ puts " #{name.ljust(14)} #{amount.to_s.ljust(10)} #{(profit / 10.0).to_s.rjust(8)}"
62
+ end
63
+
64
+ puts
65
+ puts "Total Profit: #{(total_profit / 10.0).to_s}"
66
+ puts
67
+
68
+ end
69
+
70
+ private
71
+
72
+ def ask msg
73
+ print "#{msg}"
74
+ val = STDIN.getc
75
+ end
76
+
77
+ def current_system_name
78
+ Oolite.configuration.current_system_name
79
+ end
80
+
81
+ def market
82
+ if @market.nil?
83
+ @market = MarketFile.new
84
+ @market.data
85
+ end
86
+ @market
87
+ end
88
+
89
+ def all_trades dest_system
90
+ calculate_all_trades dest_system
91
+ end
92
+
93
+ class TradeItem
94
+ attr_accessor :name
95
+ attr_accessor :amount
96
+ attr_accessor :cost
97
+ attr_accessor :revenue
98
+
99
+ def initialize name, amount, cost, revenue
100
+ @name = name
101
+ @amount = amount
102
+ @cost = cost
103
+ @revenue = revenue
104
+ end
105
+
106
+ def to_s
107
+ "#{name.ljust(14)} #{amount.to_s.ljust(10)} #{(cost / 10.0).to_s.rjust(8)} #{(revenue / 10.0).to_s.rjust(12)}"
108
+ end
109
+ end
110
+
111
+ def calculate_all_trades dest_system
112
+ src = market.data[current_system_name]
113
+ dest = market.data[dest_system]
114
+
115
+ # Remove contraband if configured.
116
+ if Oolite.configuration.trade_contraband == false
117
+ Oolite.configuration.contraband.each do |contr|
118
+ src.delete contr
119
+ end
120
+ end
121
+
122
+ positive_trades = Array.new
123
+
124
+ src.keys.each do |item|
125
+ sprice = src[item][:price]
126
+ amount = src[item][:amount]
127
+ dprice = dest[item][:price]
128
+
129
+ revenue = dprice - sprice
130
+ if revenue > 0.0 && amount > 0
131
+ positive_trades << TradeItem.new(item, amount, sprice, revenue)
132
+ end
133
+ end
134
+
135
+ positive_trades.sort { |a,b| b.revenue <=> a.revenue }
136
+ end
137
+
138
+ def calc_best_trades trades
139
+ avail_cargo = Oolite.configuration.cargo
140
+ credits = Oolite.configuration.current_credits
141
+
142
+ if avail_cargo <= 0
143
+ return [[], 0]
144
+ end
145
+
146
+ affordable_trades = []
147
+ trades.each do |trade|
148
+ if trade.cost <= credits
149
+ affordable_trades << trade
150
+ end
151
+ end
152
+
153
+ if affordable_trades.count <= 0
154
+ return [[], 0]
155
+ end
156
+
157
+ balance = credits
158
+ cargo = avail_cargo
159
+ suggested_trades_expensive_first = []
160
+ affordable_trades.each do |trade|
161
+ max_amt_by_price = balance / trade.cost
162
+ max_amt_by_cargo = [cargo, trade.amount].min
163
+ max_amt = [max_amt_by_price, max_amt_by_cargo].min
164
+
165
+ balance = balance - (max_amt * trade.cost)
166
+ cargo = cargo - max_amt
167
+
168
+ if max_amt > 0
169
+ transaction = { item: trade, amount: max_amt }
170
+ suggested_trades_expensive_first << transaction.dup
171
+ end
172
+ end
173
+
174
+ expensive_first_total_profit = 0
175
+ suggested_trades_expensive_first.each do |transaction|
176
+ profit = transaction[:item].revenue * transaction[:amount]
177
+ expensive_first_total_profit += profit
178
+ end
179
+
180
+ balance = credits
181
+ cargo = avail_cargo
182
+ suggested_trades_cheap_first = []
183
+ # Sort the trades in ascending order.
184
+ cheap_affordable_trades = affordable_trades.sort { |a,b| a.revenue <=> b.revenue }
185
+ cheap_affordable_trades.each do |trade|
186
+ max_amt_by_price = balance / trade.cost
187
+ max_amt_by_cargo = [cargo, trade.amount].min
188
+ max_amt = [max_amt_by_price, max_amt_by_cargo].min
189
+
190
+ balance = balance - (max_amt * trade.cost)
191
+ cargo = cargo - max_amt
192
+
193
+ if max_amt > 0
194
+ transaction = { item: trade, amount: max_amt }
195
+ suggested_trades_cheap_first << transaction.dup
196
+ end
197
+ end
198
+
199
+ cheap_first_total_profit = 0
200
+ suggested_trades_cheap_first.each do |transaction|
201
+ profit = transaction[:item].revenue * transaction[:amount]
202
+ cheap_first_total_profit += profit
203
+ end
204
+
205
+ if expensive_first_total_profit > cheap_first_total_profit
206
+ return [suggested_trades_expensive_first, expensive_first_total_profit]
207
+ else
208
+ return [suggested_trades_cheap_first, cheap_first_total_profit]
209
+ end
210
+ end
211
+ end
212
+ end
213
+
@@ -0,0 +1,3 @@
1
+ module Oolite
2
+ VERSION = "0.0.1"
3
+ end
data/oolite.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'oolite/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "oolite"
8
+ spec.version = Oolite::VERSION
9
+ spec.authors = ["Jeff McAffee"]
10
+ spec.email = ["jeff@ktechsystems.com"]
11
+ spec.summary = %q{Utility scripts for the Oolite game}
12
+ spec.description = %q{Utility scripts for tracking markets and assisting with trading in the Oolite game.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_runtime_dependency "nokogiri", "~> 1.6"
25
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oolite
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jeff McAffee
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ description: Utility scripts for tracking markets and assisting with trading in the
56
+ Oolite game.
57
+ email:
58
+ - jeff@ktechsystems.com
59
+ executables:
60
+ - ooconfig
61
+ - ootrader
62
+ - ooupdatemarket
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - ".gitignore"
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - bin/ooconfig
72
+ - bin/ootrader
73
+ - bin/ooupdatemarket
74
+ - lib/oolite.rb
75
+ - lib/oolite/csv_doc.rb
76
+ - lib/oolite/market.rb
77
+ - lib/oolite/market_file.rb
78
+ - lib/oolite/save_file.rb
79
+ - lib/oolite/trade.rb
80
+ - lib/oolite/version.rb
81
+ - oolite.gemspec
82
+ homepage: ''
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.3.0
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Utility scripts for the Oolite game
106
+ test_files: []
107
+ has_rdoc: