I'm coding a Google sketchup plugin with Ruby, and I faced a little problem. I have an array containing descriptions of every point like:
desc_array = ["anna ", "anna 45", "anna689", "anna36", "anna 888", "anna ",...]
The array containing every point's coordinates is:
todraw_su = [
[-16.23317, -16.530533, 99.276929],
[-25.142825, -12.476601, 99.237414],
[-32.716122, -5.92341, 99.187951],
[-38.964589, 4.181119, 99.182358],
[-41.351064, 18.350418, 99.453714],
[-40.797511, 33.987519, 99.697253],
...
]
I want to add a text in Google sketchup for each point. According to the Sketchup API this can be done by:
Sketchup.active_model.entities.add_text "This is the text", [x, y, z]
I tried:
todraw_su.each {|todraw| desc_array.each {|desc| Sketchup.active_model.entities.add_text desc,todraw }}
But, it gave me something unexpected, as it returns all the elements in desc_array for every element in to_draw.
What I want is every element in desc_array for every element in to_draw.
[desc_array, todraw_su].transpose.each do |desc, coord|
# ...
end
You can also do this with #zip like...
desc_array.zip(todraw_su).each do |desc, coord|
# ...
end
With the #zip technique, the result will always have the first array's dimension, with the second truncated or nil-padded as needed. This may or may not be TRT. Transpose will raise IndexError in that case.
Related
I need to create functionality which is going to process the dictionary (dictionary.txt file). The goal is to find all six-letter words that are built of two concatenated smaller words e.g.:
con + vex => convex
tail + or => tailor
we + aver => weaver
Of course, there may be some words inside the file that are not 6 letters long, but these can be easily sifted out using a simple method:
def cleanup_file
file_data = File.read('dictrionary.txt').split
file_data.reject! { |word| word.size < 6 }
end
But now comes the problem - how to find if the other strings in the array are made of two connected smaller words ?
[Edit]
Sample dictionary.txt file here
Thinking just in a pseudo code solution, but you should:
Iterate every line of the dictionary and store the words in 6 different arrays by the length of each word.
Make sure that all words are downcased, there are no duplicates and all the values are sorted, so later you could properly use .bsearch in the arrays.
Iterate the length-6 array (for example convex) and look for a match of the first character of the current word in the length-1 array (c for the given example) and in the length-5 array (onvex). If there's a match, save the words.
Then keep looking in the length-2 and length-4 arrays for matches (co and nvex correspondingly) and save a match.
Finally, look both parts of the string in the length-3 array (con and vex) and save any match
Look for the next 6 characters string until you've finished.
Most likely there are better ways to solve this, like in the first iteration inserting each word in its corresponding array using .bsearch_index to sort and not inserting duplicates in the same iteration, but most of the workload is going to be in the 2nd iteration and binary searches work in O(log n) time, so I guess it should work quick enough.
Suppose the dictionary is as follows.
dictionary = [
"a", "abased", "act", "action", "animal", "ape", "apeman",
"art", "assertion", "bar", "barbed", "barhop", "based", "be",
"become", "bed", "come", "hop", "ion", "man"
]
I assume that, like most dictionaries, dictionary is sorted.
First compute the following hash.
by_len = dictionary.each_with_object({}) do |w,h|
len = w.length
(h[len] ||= []) << w if len < 7
end
#=> {1=>["a"],
# 6=>["abased", "action", "animal", "apeman", "barbed",
# "barhop", "become"],
# 3=>["act", "ape", "art", "bar", "bed", "hop", "ion", "man"],
# 5=>["based"],
# 2=>["be"],
# 4=>["come"]}
Each key is a word length (1-6) and each value is an array of words from dictionary whose length is the value of the key.
Next I will define a helper function that returns true or false depending on whether a given array of words (list) contains a given word (word).
def found?(list, word)
w = list.bsearch { |w| w >= word }
w && w == word
end
For example:
found?(by_len[3], "art")
#=> true
found?(by_len[3], "any")
#=> false
See Array#bsearch.
We now extract the words of interest:
by_len[6].select { |w| (1..5).any? { |i|
found?(by_len[i], w[0,i]) && found?(by_len[6-i], w[i..-1]) } }
#=> ["abased", "action", "apeman", "barbed", "barhop", "become"]
Wondering if I could get some help with this:
function setupRound()
local gameModes = {'mode 1','mode 2','mode 3'} -- Game modes
local maps = {'map1','map2','map3'}
--local newMap = maps[math.random(1,#maps)]
local mapData = {maps[math.random(#maps)],gameModes[math.random(#gameModes)]}
local mapData = mapData
return mapData
end
a = setupRound()
print(a[1],a[2]) --Fix from Egor
What the problem is:
`
When trying to get the info from setupRound() I get table: 0x18b7b20
How I am trying to get mapData:
a = setupRound()
print(a)
Edit:
Output Issues
With the current script I will always the the following output: map3 mode 2.
What is the cause of this?
Efficiency; is this the best way to do it?
While this really isn't a question, I just wanted to know if this method that I am using is truly the most efficient way of doing this.
First of all
this line does nothing useful and can be removed (it does something, just not something you'd want)
local mapData = mapData
Output Issues
The problem is math.random. Write a script that's just print(math.random(1,100)) and run it 100 times. It will print the same number each time. This is because Lua, by default, does not set its random seed on startup. The easiest way is to call math.randomseed(os.time()) at the beginning of your program.
Efficiency; is this the best way to do it?
Depends. For what you seem to want, yes, it's definitely efficient enough. If anything, I'd change it to the following to avoid magic numbers which will make it harder to understand the code in the future.
--- etc.
local mapData = {
map = maps[math.random(#maps)],
mode = gameModes[math.random(#gameModes)]
}
-- etc.
print(a.map, a.mode)
And remember:
Premature optimization is the root of all evil.
— Donald Knuth
You did very good by creating a separate function for generating your modes and maps. This separates code and is modular and neat.
Now, you have your game modes in a table modes = {} (=which is basically a list of strings).
And you have your maps in another table maps = {}.
Each of the table items has a key, that, when omitted, becomes a number counted upwards. In your case, there are 3 items in modes and 3 items in maps, so keys would be 1, 2, 3. The key is used to grab a certain item in that table (=list). E.g. maps[2] would grab the second item in the maps table, whose value is map 2. Same applies to the modes table. Hence your output you asked about.
To get a random game mode, you just call math.random(#mode). math.random can accept up to two parameters. With these you define your range, to pick the random number from. You can also pass a single parameter, then Lua assumes to you want to start at 1. So math.random(3) becomes actually math.random(1, 3). #mode in this case stand for "count all game modes in that table and give me that count" which is 3.
To return your chosen map and game mode from that function we could use another table, just to hold both values. This time however the table would have different keys to access the values inside it; namely "map" and "mode".
Complete example would be:
local function setupRound()
local modes = {"mode 1", "mode 2", "mode 3"} -- different game modes
local maps = {"map 1", "map 2", "map 3"} -- different maps
return {map = maps[math.random(#maps)], mode = modes[math.random(#modes)]}
end
for i = 1, 10 do
local freshRound = setupRound()
print(freshRound.map, freshRound.mode)
end
I'm currently writing an Addon for World of Warcraft, but I've run a bit of a basic lua problem. The gist of it is, I want to reduce the following elements inside this for loop into a single line.
Currently, I am using WoW's api command of "SetAlpha(0)" (which just hides the frame in game) on a list of frames that share the same name except for the number at the end. ActionButton1Name, ActionButton2Name, ActionButton3Name, etc.
I also have to do the same thing to four other lists, each also having 12 different versions of themselves MultiBarBottomRightButton1Name, MultiBarBottomRightButton2Name, etc.
Essentially, I'd like to take what I currently have:
for i=1, 12 do
["ActionButton"..i.."Name"]:SetAlpha(0)
["MultiBarBottomRightButton"..i.."Name"]:SetAlpha(0)
["MultiBarBottomLeftButton"..i.."Name"]:SetAlpha(0)
["MultiBarRightButton"..i.."Name"]:SetAlpha(0)
["MultiBarLeftButton"..i.."Name"]:SetAlpha(0)
end
SetAlpha is specific to World of Warcraft's API, so disregard that as needed.
And transform it into something where I have all the [ ] elements in their own table somewhere else, and then later use that index
local buttonNameIndex = function()
for i=1, 12 do
["ActionButton"..i.."Name"]
["MultiBarBottomRightButton"..i.."Name"]
["MultiBarBottomLeftButton"..i.."Name"]
["MultiBarRightButton"..i.."Name"]
["MultiBarLeftButton"..i.."Name"]
end
end
for i,v in ipairs(buttonNameIndex) do
v:SetAlpha(0) end
However, the above function didn't seem to work, in that the terminal was spitting out an error of having an unexpected symbol near the do of the 2nd line.
As to why I'd like to do this: I'll be doing a lot more with that list of frames later on, so it'd be nice to have them all combined into a single table.
If these are global variables, use expressions like this:
_G["ActionButton"..i.."Name"]:SetAlpha(0)
which access a global variable from a dynamically created name.
I wrote a method that takes six names then generates an array of seven random numbers using four 6-sided dice. The lowest value of the four 6-sided dice is dropped, then the remainder is summed to create the value. The value is then added to an array.
Once seven numbers have been generated, the array is then ordered from highest to lowest and the lowest value is dropped. Then the array of names and the array of values are zipped together to create a hash.
This method ensures that the first name in the array of names receives the highest value, and the last name receives the lowest.
This is the result of calling the method:
{:strength=>1, :dexterity=>1, :constitution=>0, :intelligence=>0, :wisdom=>0, :charisma=>1}
As you can see, all the values I receive are either "1" or "0". I have no idea how this is happening.
Here is the code:
module PriorityStatGenerator
def self.roll_stats(first_stat, second_stat, third_stat, fourth_stat, fifth_stat, sixth_stat)
stats_priority = [first_stat, second_stat, third_stat, fourth_stat, fifth_stat, sixth_stat].map(&:to_sym)
roll_array = self.roll
return Hash[stats_priority.zip(roll_array)]
end
private
def self.roll
roll_array = []
7.times {
roll_array << Array.new(4).map{ 1 + rand(6) }.sort.drop(1).sum
}
roll_array.reverse.delete_at(6)
end
end
This is how I'm calling the method while I'm testing:
render plain: PriorityStatGenerator.roll_stats(params[:prioritize][:first_stat], params[:prioritize][:second_stat], params[:prioritize][:third_stat], params[:prioritize][:fourth_stat], params[:prioritize][:fifth_stat], params[:prioritize][:sixth_stat])
I added require 'priority_stat_generator' where I'm calling the method, so it is properly calling it.
Can someone help me make it return proper values between 1 and 18?
Here's a refactoring to simplify things and use an actually random number generator, as rand is notoriously terrible:
require 'securerandom'
module PriorityStatGenerator
def self.roll_stats(*stats)
Hash[
stats.map(&:to_sym).zip(self.roll(stats.length).reverse)
]
end
private
def self.roll(n = 7)
(n + 1).times.map do
4.times.map { 1 + SecureRandom.random_number(6) }.sort.drop(1).inject(:+)
end.sort.last(n)
end
end
This makes use of inject(:+) so it works in plain Ruby, no ActiveSupport required.
The use of *stats makes the roll_stats function way more flexible. Your version has a very rigid number of parameters, which is confusing and often obnoxious to use. Treating the arguments as an array avoids a lot of the binding on the expectation that there's six of them.
As a note it's not clear why you're making N+1 roles and then discarding the last. That's the same as generating N and discarding none. Maybe you meant to sort them and take the N best?
Update: Added sort and reverse to properly map in terms of priority.
You need to learn to use IRB or PRY to test snippets of your code, or better, learn to use a debugger. They give you insight into what your code is doing.
In IRB:
[7,6,5,4,3,2,1].delete_at(6)
1
In other words, delete_at(6) is doing what it's supposed to, but that's not what you want. Instead, perhaps slicing the array will behave more like you expect:
>> [7,6,5,4,3,2,1][0..-2]
[
[0] 7,
[1] 6,
[2] 5,
[3] 4,
[4] 3,
[5] 2
]
Also, in your code, it's not necessary to return a value when that operation is the last logical step in a method. Ruby will return the last value seen:
Hash[stats_priority.zip(roll_array)]
As amadan said, I can't see how you are getting the results you are, but their is a definite bug in your code.
The last line in self.roll is the return value.
roll_array.reverse.delete_at(6)
Which is going to return the value that was deleted. You need to add a new lines to return the roll_array instead of the delete_at value. You are also not sorting your array prior to removing that last item which will give you the wrong values as well.
def self.roll
roll_array = []
7.times {
roll_array << Array.new(4).map{ 1 + rand(6) }.sort.drop(1).sum
}
roll_array.sort.drop(1)
roll_array
end
I have been searching on SO regarding the differences in output of the json arrays but haven't got any results useful to my usecase for it . Here's my issue , I have been using Google maps to display shapes and then show the selected portion into another into another map .
The output that Google map shows of selected portion -
[{"type":"RECTANGLE","id":null,"geometry":[[32.33733004161649,50.90904235665221],[32.377641904110355,50.989036558312364]]}]
The output that i recieve while using the to_json method -
"[{\"id\":null,\"type\":\"RECTANGLE\",\"radius\":\"3513.3954239013615\",\"geometry\":\"[32.39126846779702, 50.91075897042174]\"}]"
How or what can i do , to make the output of both the methods same so that i can display the data in the format it's intended to . New to json encode and decoding in rails , any help is much appreciated .
Update 1 - After Omitting the to_json call
[{"id"=>nil, "type"=>"CIRCLE", "radius"=>"3513.3954239013615", "geometry"=>"[32.39126846779702, 50.91075897042174]"}]
Still , the "=>" won't be understood by google maps
Let’s consider you have a following array of hashes in ruby:
a = [{
"id"=>nil,
"type"=>"CIRCLE",
"radius"=>"3513.3954239013615",
"geometry"=>"[32.39126846779702, 50.91075897042174]"
}]
You now need to make it understandable by google map. Here we go:
result = a.map { |e| # will convert geometry to what G expects
ll = JSON.parse e["geometry"]
e["geometry"] = [ll] # passing just a single variable
# you are likely to calc those basing on your radius value
e
}.map(&:to_json)
Now don’t be confused by output of ruby p, it shows escaped strings. To assure, everything goes OK:
result.each { |e| puts e }
#⇒ {
# "id":null,
# "type":"CIRCLE",
# "radius":"3513.3954239013615",
# "geometry":[
# [32.39126846779702,50.91075897042174],
# [32.39126846779702,50.91075897042174]
# ]
# }
Hope it helps.