monkeymusic 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ module MonkeyMusic
2
+ class ScoreSystem
3
+
4
+ def evaluate_user_recommendations!(user)
5
+ @user = user
6
+ @user.recommendations.each { |track| evaluate!(track) }
7
+ end
8
+
9
+ def evaluate!(track)
10
+ multiplier = 0
11
+ if @user.toplists[:top_tracks].include? track.name
12
+ multiplier = -1
13
+ elsif @user.toplists[:disliked].include? track.artist
14
+ multiplier = -2
15
+ else
16
+ if @user.toplists[:top_albums].include? track.album
17
+ multiplier += 1
18
+ end
19
+ if @user.toplists[:top_artists].include? track.artist
20
+ multiplier += 1
21
+ end
22
+ if @user.toplists[:top_track_albums].include? track.album
23
+ multiplier += 1
24
+ end
25
+ end
26
+ track.multiplier = multiplier
27
+ track.value = 4**multiplier.abs * sign(multiplier)
28
+ end
29
+
30
+ private
31
+
32
+ def sign(num)
33
+ num < 0 ? -1 : 1
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,68 @@
1
+ require 'hallon'
2
+
3
+ module MonkeyMusic
4
+ class ToplistLoader
5
+ attr_reader :loaded_toplists
6
+
7
+ def initialize(toplist_file)
8
+ @toplist_file = IO.read(toplist_file)
9
+ @toplists = {}
10
+ end
11
+
12
+ def load_for_user!(user)
13
+ @user = user
14
+ # Load toplists URI:s from file
15
+ self.instance_eval(@toplist_file)
16
+ # Load toplist contents from libspotify
17
+ load_toplists!
18
+ load_top_track_albums!
19
+ end
20
+
21
+ private
22
+
23
+ # User toplist DSL
24
+
25
+ def toplist(type, uri)
26
+ @toplists[type] = uri
27
+ end
28
+
29
+ # Toplist loading
30
+
31
+ def load_toplists!
32
+ @loaded_toplists = {}
33
+ @toplists.each do |type, uri|
34
+ @loaded_toplists[type] = load_from_list(type, uri)
35
+ @user.toplists[type] = parse_toplist(@loaded_toplists[type])
36
+ end
37
+ end
38
+
39
+ def load_top_track_albums!
40
+ @loaded_toplists[:top_track_albums] =
41
+ @loaded_toplists[:top_tracks].map(&:album).each(&:load)
42
+ @user.toplists[:top_track_albums] =
43
+ parse_toplist(@loaded_toplists[:top_track_albums])
44
+ end
45
+
46
+ def load_from_list(type, uri)
47
+ playlist = Hallon::Playlist.new(uri).load
48
+ tracks = playlist.tracks.to_a
49
+ tracks.each(&:load)
50
+ if type == :top_tracks
51
+ tracks
52
+ elsif type == :top_artists || type == :disliked
53
+ tracks.map(&:artist).each(&:load)
54
+ elsif type == :top_albums
55
+ tracks.map(&:album).each(&:load)
56
+ end
57
+ end
58
+
59
+ def parse_toplist(list)
60
+ result = []
61
+ list.each do |item|
62
+ result << item.name
63
+ end
64
+ result
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,36 @@
1
+ require 'em-websocket'
2
+ require 'json'
3
+ require 'rack'
4
+
5
+ module MonkeyMusic
6
+ class BrowserUI
7
+
8
+ def initialize(delay)
9
+ @delay = delay || 1
10
+ puts "Initializing websockets..."
11
+ Thread.new {
12
+ EM.run {
13
+ EM::WebSocket.run(:host => "0.0.0.0", :port => 3000) do |ws|
14
+ @ws = ws
15
+ ws.onopen do |handshake|
16
+ end
17
+
18
+ ws.onclose do
19
+ end
20
+
21
+ ws.onmessage do |msg|
22
+ end
23
+ end
24
+ }
25
+ }
26
+ sleep 1 # TODO: Asynchronous acknowledge that ui is ready
27
+ puts "Websockets initialized!"
28
+ end
29
+
30
+ def update(level)
31
+ @ws.send(level.as_json) if @ws
32
+ sleep @delay
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ module MonkeyMusic
2
+ class ConsoleUI
3
+
4
+ def initialize(delay)
5
+ @delay = delay || 1
6
+ end
7
+
8
+ def msg(msg)
9
+ puts "\e[H\e[2J"
10
+ puts "\n"*5
11
+ puts " "*10 + msg
12
+ puts "\n"*5
13
+ end
14
+
15
+ def update(level)
16
+ # Clear screen
17
+ puts "\e[H\e[2J"
18
+ # Level
19
+ puts level.to_s
20
+ # Score
21
+ puts "\n"
22
+ puts "="*10
23
+ puts "\n"
24
+ level.players.each do |player|
25
+ puts "#{player.monkey.name}: #{player.monkey.score}"
26
+ puts "{ #{"0"*player.monkey.carrying.count}#{"_"*player.monkey.remaining_capacity} }\n"
27
+ end
28
+ sleep @delay
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,71 @@
1
+ module MonkeyMusic
2
+ class Base
3
+ attr_accessor :x, :y, :character
4
+ attr_reader :level, :id
5
+
6
+ @@curr_id = 0
7
+
8
+ def assign_id
9
+ @id = @@curr_id
10
+ @@curr_id += 1
11
+ end
12
+
13
+ def place!(level, x, y)
14
+ @level = level
15
+ @x, @y = x, y
16
+ end
17
+
18
+ def move!
19
+ # To be overriden
20
+ end
21
+
22
+ def at?(x, y)
23
+ @x == x && @y == y
24
+ end
25
+
26
+ def distance_to(unit)
27
+ (@x - unit.x).abs + (@y - unit.y).abs
28
+ end
29
+
30
+ def pos
31
+ [@x, @y]
32
+ end
33
+
34
+ def direction_of(unit)
35
+ if (@x - unit.x).abs > (@y - unit.y).abs
36
+ unit.x > @x ? :east : :west
37
+ else
38
+ unit.y > @y ? :south : :north
39
+ end
40
+ end
41
+
42
+ def to_s
43
+ @character
44
+ end
45
+
46
+ # As shown to player
47
+ def serialize
48
+ to_s
49
+ end
50
+
51
+ def to_json(options = {})
52
+ { :id => @id,
53
+ :x => @x,
54
+ :y => @y,
55
+ :type => self.class.name.split('::').last
56
+ }.to_json
57
+ end
58
+
59
+ private
60
+
61
+ def translate(x, y, direction)
62
+ case direction
63
+ when :north then [x, y - 1]
64
+ when :south then [x, y + 1]
65
+ when :east then [x + 1, y ]
66
+ when :west then [x - 1, y ]
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,85 @@
1
+ module MonkeyMusic
2
+ class Monkey < Base
3
+ attr_accessor :name, :score, :capacity, :character
4
+ attr_reader :carrying, :facing
5
+
6
+ def self.player(number)
7
+ Class.new Monkey do @player_index = number - 1 end
8
+ end
9
+
10
+ def self.from_players(players)
11
+ @player_index &&
12
+ players[@player_index] &&
13
+ players[@player_index].monkey
14
+ end
15
+
16
+ def initialize
17
+ @score = 0
18
+ @capacity = 1
19
+ @carrying = []
20
+ @delivered = []
21
+ @facing = [:west, :east].sample
22
+ end
23
+
24
+ def move!(direction)
25
+ target = translate(@x, @y, direction)
26
+ if target_unit = @level.at(*target)
27
+ # Interact with unit
28
+ interact_with!(target_unit)
29
+ else
30
+ # Face the right direction
31
+ @facing = case direction
32
+ when :west then :west
33
+ when :east then :east
34
+ else @facing
35
+ end
36
+ # Perform move
37
+ @x, @y = *target if @level.accessible?(*target)
38
+ end
39
+ end
40
+
41
+ def interact_with!(unit)
42
+ case unit
43
+ when Track then pick_up!(unit)
44
+ when User then deliver!
45
+ end
46
+ end
47
+
48
+ def pick_up!(unit)
49
+ if @carrying.count < @capacity
50
+ @level.remove(unit)
51
+ @carrying << unit
52
+ end
53
+ end
54
+
55
+ def deliver!
56
+ @delivered += @carrying
57
+ @score += tally(@carrying)
58
+ @carrying = []
59
+ end
60
+
61
+ def tally(tracks)
62
+ score = 0
63
+ tracks.each do |track|
64
+ score += track.value
65
+ end
66
+ score
67
+ end
68
+
69
+ def remaining_capacity
70
+ (@capacity - carrying.count) || 0
71
+ end
72
+
73
+ def to_json(options = {})
74
+ { :id => @id,
75
+ :x => @x,
76
+ :y => @y,
77
+ :facing => @facing,
78
+ :type => self.class.name.split('::').last,
79
+ :name => @name,
80
+ :score => @score,
81
+ }.to_json
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,33 @@
1
+ module MonkeyMusic
2
+ class Track < Base
3
+ attr_accessor :uri, :name, :artist, :album, :popularity, :year,
4
+ :value, :multiplier
5
+
6
+ def self.worth(n)
7
+ Class.new Track do @worth = n end
8
+ end
9
+
10
+ def self.from_user(user)
11
+ if @worth
12
+ user.recommend!(@worth) || user.recommendations.sample
13
+ else
14
+ user.recommendations.sample
15
+ end
16
+ end
17
+
18
+ def serialize
19
+ @uri
20
+ end
21
+
22
+ def to_json(options = {})
23
+ { :id => @id,
24
+ :x => @x,
25
+ :y => @y,
26
+ :type => self.class.name.split('::').last,
27
+ :name => @name,
28
+ :multiplier => @multiplier,
29
+ :value => @value,
30
+ }.to_json
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ require 'yaml'
2
+
3
+ module MonkeyMusic
4
+ class User < Base
5
+ attr_accessor :toplists, :recommendations
6
+
7
+ def initialize
8
+ @toplists = {}
9
+ @recommendations = @remaining_recommendations = []
10
+ end
11
+
12
+ def recommend!(worth)
13
+ index = @remaining_recommendations.index do |r|
14
+ r.multiplier == worth
15
+ end
16
+ if index
17
+ @remaining_recommendations.delete_at(index)
18
+ else
19
+ @recommendations.find { |r| r.multiplier == worth }
20
+ end
21
+ end
22
+
23
+ def dump
24
+ YAML::dump({
25
+ :toplists => @toplists,
26
+ :recommendations => @recommendations
27
+ })
28
+ end
29
+
30
+ def load_from_file(file)
31
+ data = YAML::load(IO.read(file))
32
+ @toplists = data[:toplists]
33
+ @recommendations = data[:recommendations]
34
+ @remaining_recommendations = @recommendations
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ module MonkeyMusic
2
+ class Wall < Base
3
+ end
4
+ end
@@ -0,0 +1,20 @@
1
+ $: << File.dirname(__FILE__)
2
+
3
+ require 'set'
4
+
5
+ require 'monkey_music/runner'
6
+ require 'monkey_music/game'
7
+ require 'monkey_music/player'
8
+ require 'monkey_music/toplist_loader'
9
+ require 'monkey_music/recommendation_loader'
10
+ require 'monkey_music/score_system'
11
+ require 'monkey_music/level'
12
+ require 'monkey_music/level_loader'
13
+
14
+ require 'monkey_music/ui/console_ui'
15
+ require 'monkey_music/ui/browser_ui'
16
+
17
+ require 'monkey_music/units/base'
18
+ require 'monkey_music/units/monkey'
19
+ require 'monkey_music/units/track'
20
+ require 'monkey_music/units/user'
data/users/synth.rb ADDED
@@ -0,0 +1,7 @@
1
+ toplist :top_tracks, "spotify:user:poscar%21:playlist:4yIhuHviCmTYjcFqunPxsa"
2
+
3
+ toplist :top_artists, "spotify:user:poscar%21:playlist:6MjfrIbeCKQMVCU9JQNf1u"
4
+
5
+ toplist :top_albums, "spotify:user:poscar%21:playlist:1ZXPeNHa8YYvY0VzxRdv3q"
6
+
7
+ toplist :disliked, "spotify:user:poscar%21:playlist:5IEx3cooSMgrtftvw5B9Ug"