Formula for calculating user experience - ruby-on-rails

I would like (without using a gem) to create a level and experience point system for my users.
When creating his account, he starts level 0 with 0 experience points. To reach level 1 he will need 10 additional points, for a total of 10 points. For level 3 he will need 30 additional points, for a total of 60 experience points. Each additional level requires 10 more experience points. (see example below).
Level | Total XP | XP for next level|
----------------------------------------|
0 | 0 | 10 |
1 | 10 | 20 |
2 | 30 | 30 |
3 | 60 | 40 |
4 | 100 | 50 |
5 | 150 | 60 |
etc...
I would like an xp column in my user table that would represent the total experience of a user.
In my view I would like to display its level. But what is the formula for calculating this?
Let's imagine that my user has 157 experience points in total. Which corresponds to a level 5. How to calculate a level only via the total experience points and how to calculate this regardless of its total experience point whether it is 38 like 369 or 4393.

I'm not sure if this is exactly what you wanted. It's more of an algorithm than a formula, but here is a method that will return the proper level based on the experience given. Think of the bar variable like the bar you have to get over to meet a new level, and think of the step variable like the amount of experience added between each level (as you describe in your right-most column).
def calculate_level(experience)
level = 0
bar = 10
step = 10
while experience >= bar
level += 1
step += 10
bar += step
end
level
end
Example output:
irb> calculate_level(0) # => 0
irb> calculate_level(9) # => 0
irb> calculate_level(10) # => 1
irb> calculate_level(11) # => 1
irb> calculate_level(29) # => 1
irb> calculate_level(30) # => 2
irb> calculate_level(31) # => 2
irb> calculate_level(59) # => 2
irb> calculate_level(60) # => 3
irb> calculate_level(61) # => 3
irb> calculate_level(99) # => 3
irb> calculate_level(100) # => 4
irb> calculate_level(101) # => 4
irb> calculate_level(149) # => 4
irb> calculate_level(150) # => 5
irb> calculate_level(151) # => 5

Calculate the inverse of my above comment and take the floor:
def calculate_level experience
(Math.sqrt(0.2 * experience + 0.25) - 0.5).floor
end

You can use such "formula"
def calculate_level(experience)
(1..).find { |i| (experience -= 10 * i).negative? } - 1
end
Just decrease experience on 10x steps every time and check if result is not negative
calculate_level(0)
# => 0
calculate_level(9)
# => 0
calculate_level(10)
# => 1
calculate_level(11)
# => 1
calculate_level(29)
# => 1
calculate_level(30)
# => 2
calculate_level(31)
# => 2
calculate_level(59)
# => 2
calculate_level(60)
# => 3
calculate_level(61)
# => 3
calculate_level(99)
# => 3
calculate_level(100)
# => 4
calculate_level(101)
# => 4
calculate_level(149)
# => 4
calculate_level(150)
# => 5
calculate_level(151)
# => 5
calculate_level(209)
# => 5
calculate_level(210)
# => 6

Related

I'm testing machine learning multilayer perceptron using rubix/ml and have a question for scores and losses

well.. This is my first attempt to ml modelling. Given data is like below..
// normalized stock price data with label
// is just EXAMPLE
// $labels = ['10%', '30%', '-30%', ..];
// $samples= [[1,0,45,29,100], [100,13,0,14,5], [3,2,0,10,100], ..];
Each sample has 31 value and all label has 7 types. I successfully trained Model itself but score doesn't add up. Every attempt make under 0.2 score.
array:4 [▼
"environment" => array:4 [▶]
"filename" => "MP-test-220425-010403.rbx"
"scores" => array:9 [▼
1 => 0.077863577863578
2 => 0.077863577863578
3 => 0.071805006587615
4 => 0.14278357892554
5 => 0.077863577863578
6 => 0.077863577863578
7 => 0.13096842384232
8 => 0.078355812459859
9 => 0.077863577863578
]
"losses" => array:9 [▼
1 => 0.2336202611478
2 => 0.22053903758692
3 => 0.22142868877431
4 => 0.21962296766134
5 => 0.21888143998952
6 => 0.21872846102315
7 => 0.21900067894143
8 => 0.21882642822037
9 => 0.21780065553406
]
]
This is what I got .. Scores keep getting same value. What does it mean? I can find examples about underfitting and overfitting but they all just explaining on somewhat high accuracy.. at least above 50% ;;;;
Is.. underfitting or overfitting? Do I need more training data? Data is actually cannot classifiable? ..
What can I do to improving result?

How to Calculate sum of all the digits in text file

I am having text file t.txt,I want to calculate sum of all the digits in text file
Example
--- t.txt ---
The rahul jumped in 2 the well. The water was cold at 1 degree Centigrade. There were 3 grip holes on the walls. The well was 17 feet deep.
--- EOF --
sum 2+1+3+1+7
My ruby code to calculate sum is
ruby -e "File.read('t.txt').split.inject(0){|mem, obj| mem += obj.to_f}"
But i am not getting any answer??
str = "The rahul jumped in 2 the well. The water was cold at 1 degree Centigrade. There were 3 grip holes on the walls. The well was 17 feet deep."
To get sum of all integers:
str.scan(/\d+/).sum(&:to_i)
# => 23
Or to get sum of all digits as in your example:
str.scan(/\d+?/).sum(&:to_i)
# => 14
PS: I used sum seeing Rails tag. If you are only using Ruby you can use inject instead.
Example with inject
str.scan(/\d/).inject(0) { |sum, a| sum + a.to_i }
# => 14
str.scan(/\d+/).inject(0) { |sum, a| sum + a.to_i }
# => 23
Your statement is computing correctly. Just add puts before File read as:
ruby -e "puts File.read('t.txt').split.inject(0){|mem, obj| mem += obj.to_f}"
# => 23.0
For summing single digit only:
ruby -e "puts File.read('t.txt').scan(/\d/).inject(0){|mem, obj| mem += obj.to_f}"
# => 14.0
Thanks

Elixir/Erlang: Fast lookup with static table

In my application I need to convert integer to some term; for example:
1 → :red
2 → :green
3 → :blue
The table is static, it is known during compilation time and its indices range from <1, n>. There is around 60 of them.
In which way should the table be represented, so the lookup is the fastest? Dict, HashDict, tuple (with kernel.elem()), ets, function with pattern matching...?
As Julius Beckmann suggested in this case functions with pattern matching should be sufficient as they are according to my measurements over 5 times faster than accessing a map.
Below you can find an implementation of what you are looking for (benchmark code included at the bottom):
defmodule TermLookUpByInteger do
#term_by_integer %{
1 => :aa, 11 => :ba, 21 => :ca, 31 => :da, 41 => :ea, 51 => :fa, 61 => :ga,
2 => :ab, 12 => :bb, 22 => :cb, 32 => :db, 42 => :eb, 52 => :fb, 62 => :gb,
3 => :ac, 13 => :bc, 23 => :cc, 33 => :dc, 43 => :ec, 53 => :fc, 63 => :gc,
4 => :ad, 14 => :bd, 24 => :cd, 34 => :dd, 44 => :ed, 54 => :fd, 64 => :gd,
5 => :ae, 15 => :be, 25 => :ce, 35 => :de, 45 => :ee, 55 => :fe, 65 => :ge,
6 => :af, 16 => :bf, 26 => :cf, 36 => :df, 46 => :ef, 56 => :ff, 66 => :gf,
7 => :ag, 17 => :bg, 27 => :cg, 37 => :dg, 47 => :eg, 57 => :fg, 67 => :gg,
8 => :ah, 18 => :bh, 28 => :ch, 38 => :dh, 48 => :eh, 58 => :fh, 68 => :gh,
9 => :ai, 19 => :bi, 29 => :ci, 39 => :di, 49 => :ei, 59 => :fi, 69 => :gi,
0 => :aj, 10 => :bj, 20 => :cj, 30 => :dj, 40 => :ej, 50 => :fj, 60 => :gj,
}
#doc """
iex> TermLookUpByInteger.lookup_pmf(2)
:ab
"""
def lookup_pmf(int), do: do_lookup(int)
for {int, term} <- #term_by_integer do
defp do_lookup(unquote(int)), do: unquote(term)
end
#doc """
iex> TermLookUpByInteger.lookup_m(3)
:ac
"""
def lookup_m(int), do: #term_by_integer[int]
end
# Benchmark:
n = 1_000_000
range = 1..(n)
measure = fn fun -> :timer.tc(fn -> for _ <- range, do: fun.() end) end
{time_pmf, _result} = measure.(fn -> TermLookUpByInteger.lookup_pmf(:random.uniform(60)) end)
{time_m, _result} = measure.(fn -> TermLookUpByInteger.lookup_m(:random.uniform(60)) end)
IO.puts " Sample size: #{n}"
IO.puts "Pattern matching functions lookup: #{time_pmf/1000} ms"
IO.puts " Map lookup: #{time_m/1000} ms"
IO.puts " Absolute Difference: #{(time_pmf-time_m)/1000} ms"
IO.puts " Relative Difference: #{round((time_pmf-time_m)/time_m*100)}%"
IO.puts " Faster: x #{Float.round(time_m/time_pmf, 2)} times"
Results:
Sample size: 1000000
Pattern matching functions lookup: 447.6 ms
Map lookup: 2423.517 ms
Absolute Difference: -1975.917 ms
Relative Difference: -82%
Faster: x 5.41 times
I hope that will be useful.
If the map is completely static and will not change, you can use generated pattern matching. This will be the fastest way to integrate that lookup in your application.
Some example code, reading these mappings from a external file: https://github.com/h4cc/slugger/blob/master/lib/slugger.ex#L69-72
Instead of using a external file, your source map data can be held in a #attribute.
Even if new mappings will be needed on runtime, these could be handled with a catchall-pattern match doing a lookup in a HashDict.
If you rely on fast access from many processes, then mochiglobal may be the answer. It's tricky constant pool, which keeps keys and values as functions in module. Every time you put/2 something, module is recompiled (it's slow but doesn't matter in your case). With this approach value is not copied into process heap as it is function call. Here is better explanation.

How to specify markers in YAML file

I have a YAML file that is going to be parsed by two different machines, so I want to specify some sort of markers in the file to indicate which machine has the right to read a specific block.
As an example I want the block 1 to be parsed by machine 1 and block 2 to be parsed by machine2:
# BLOCK 1 - Machine 1
-
:id: 1234
:worker: Foo1
:opts:
:ftpaccount: user1
:limit: 10
# BLOCK 2 - Machine 2
-
:id: 5678
:worker: Foo2
:opts:
:ftpaccount: user2
:limit: 10
How can I achieve something like this? How you implement something similar to this? Thanks.
Treat the blocks as hash entries with the key being the hostname:
require 'yaml'
yaml = <<EOT
host1:
# BLOCK 1 - Machine 1
-
:id: 1234
:worker: Foo1
:opts:
:ftpaccount: user1
:limit: 10
host2:
# BLOCK 2 - Machine 2
-
:id: 5678
:worker: Foo2
:opts:
:ftpaccount: user2
:limit: 10
EOT
config = YAML.load(yaml)
# => {"host1"=>
# [{:id=>1234,
# :worker=>"Foo1",
# :opts=>{:ftpaccount=>"user1", :limit=>10}}],
# "host2"=>
# [{:id=>5678,
# :worker=>"Foo2",
# :opts=>{:ftpaccount=>"user2", :limit=>10}}]}
At this point you can grab the chunk you need:
config['host1']
# => [{:id=>1234, :worker=>"Foo1", :opts=>{:ftpaccount=>"user1", :limit=>10}}]
config['host2']
# => [{:id=>5678, :worker=>"Foo2", :opts=>{:ftpaccount=>"user2", :limit=>10}}]
You don't even have to hard-code the hostname; You can ask the machine what its name is:
`hostname`.chomp # => "MyHost"
Actually, I'd change the YAML a little, so it's a hash of hashes. As is, your YAML returns a hash of arrays of hashes, which, because of the array, makes it more awkward to use:
host1:
# BLOCK 1 - Machine 1
:id: 1234
:worker: Foo1
:opts:
:ftpaccount: user1
:limit: 10
host2:
# BLOCK 2 - Machine 2
:id: 5678
:worker: Foo2
:opts:
:ftpaccount: user2
:limit: 10
Results in:
config = YAML.load(yaml)
# => {"host1"=>
# {:id=>1234, :worker=>"Foo1", :opts=>{:ftpaccount=>"user1", :limit=>10}},
# "host2"=>
# {:id=>5678, :worker=>"Foo2", :opts=>{:ftpaccount=>"user2", :limit=>10}}}
config['host1']
# => {:id=>1234, :worker=>"Foo1", :opts=>{:ftpaccount=>"user1", :limit=>10}}
config['host2']
# => {:id=>5678, :worker=>"Foo2", :opts=>{:ftpaccount=>"user2", :limit=>10}}
Finally, if your YAML file is complex, or long, or has repeated sections, seriously consider writing code that emits that file for you. Ruby makes it really easy to generate the YAML in a very smart way that automatically uses aliases. For instance:
require 'yaml'
SOME_COMMON_DATA = {
'shared_db_dsn' => 'mysql://user:password#host/db'
}
HOST1 = 'foo.com'
HOST1_DATA = {
HOST1 => {
'id' => 1234,
'worker' => 'Foo1',
'opts' => {
'ftpaccount' => 'user1',
'limit' => 10
},
'dsn' => SOME_COMMON_DATA
}
}
HOST2 = 'bar.com'
HOST2_DATA = {
HOST2 => {
'id' => 5678,
'worker' => 'Foo2',
'opts' => {
'ftpaccount' => 'user2',
'limit' => 10
},
'dsn' => SOME_COMMON_DATA
}
}
data = {
HOST1 => HOST1_DATA,
HOST2 => HOST2_DATA,
}
puts data.to_yaml
# >> ---
# >> foo.com:
# >> foo.com:
# >> id: 1234
# >> worker: Foo1
# >> opts:
# >> ftpaccount: user1
# >> limit: 10
# >> dsn: &1
# >> shared_db_dsn: mysql://user:password#host/db
# >> bar.com:
# >> bar.com:
# >> id: 5678
# >> worker: Foo2
# >> opts:
# >> ftpaccount: user2
# >> limit: 10
# >> dsn: *1
Notice how YAML converted "dsn" into an alias and referenced it in the second host's definition using an anchor. This can add up to serious space savings, depending on how you define your variables and build the data structure. See "Aliases and Anchors" for more information.
Also, I'd highly recommend avoiding the use of symbols for your hash keys. By doing so your YAML can be easily loaded by other languages, not just Ruby. At that point, your YAML becomes even more useful when building big systems.
Here's a simple state machine that assembles a string based on the most recent matching comment in the yaml file. The YAML string is then loaded into the parser. If your files are really large, you could easily modify this to use Tempfile or some other IO class.
require 'yaml'
class YAMLSplitter
attr_reader :flag, :mode, :raw
def initialize(flag)
#flag = flag
#mode = :match
#raw = ""
end
def parse(file)
File.read(file).each_line do |line|
process_line(line)
end
YAML.load(raw)
end
private
def process_line(line)
set_match_status(line)
write_line(line) if match?
end
def set_match_status(line)
if line.start_with?("#")
if line.match(flag)
match!
else
nomatch!
end
end
end
def write_line(line)
puts "WRITE_LINE #{mode.inspect} #{line.inspect}"
raw << line
end
def match?
mode == :match
end
def match!
#mode = :match
end
def nomatch!
#mode = :nomatch
end
end
YAML:
---
# machine 1
- 1
- 2
- 3
- 4
# machine 2
- 5
- 6
- 7
- 8
- 9
- 10
- 11
# machine 1
- 12
Execution:
splitter = YAMLSplitter.new('machine 1')
yaml = splitter.parse('test.yml')

Friendly Byte Formatting in Rails

I need to format an integer representation of bytes into something friendly, and I'm hoping that there's a utility function in Ruby or in Rails that will do that formatting for me (to perpetuate my laziness, of course.)
I'm looking for something that would look like:
format_bytes(1024) -> "1 KB"
format_bytes(1048576) -> "1 MB"
Looks like there's some stuff in ActiveSupport to do it the other way around, but I haven't found a way to do it in this direction.
If there isn't one that exists, does anyone have a particularly elegant solution?
Number to human size is what you're looking for.
require 'action_view'
include ActionView::Helpers::NumberHelper
number_to_human_size(123) # => 123 Bytes
number_to_human_size(1234) # => 1.2 KB
number_to_human_size(12345) # => 12.1 KB
number_to_human_size(1234567) # => 1.2 MB
number_to_human_size(1234567890) # => 1.1 GB
number_to_human_size(1234567890123) # => 1.1 TB
number_to_human_size(1234567, :precision => 2) # => 1.18 MB
number_to_human_size(483989, :precision => 0) # => 473 KB
number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB
Accepted answer still work, but requires actionpack instead of actionview in newer rails.
require 'actionpack'
Accepted answer it's perfect, but I didn't need the first two lines.
I only put:
number_to_human_size(123) # => 123 Bytes
number_to_human_size(1234) # => 1.2 KB
number_to_human_size(12345) # => 12.1 KB
number_to_human_size(1234567) # => 1.2 MB
number_to_human_size(1234567890) # => 1.1 GB
number_to_human_size(1234567890123) # => 1.1 TB
number_to_human_size(1234567, :precision => 2) # => 1.18 MB
number_to_human_size(483989, :precision => 0) # => 473 KB
number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB
and works like a charm.

Resources