diff --git a/2017-uge1/conf.lua b/2017-uge1/conf.lua new file mode 100644 index 0000000..14bbe6b --- /dev/null +++ b/2017-uge1/conf.lua @@ -0,0 +1,42 @@ +function love.conf(t) + t.identity = "dildo" + t.version = "11.1" + t.console = false + t.accelerometerjoystick = false + t.externalstorage = false + t.gammacorrect = false + + t.window.title = "Dildo Collector" + t.window.icon = nil--'images/icon.png' + t.window.width = 500 + t.window.height = 500 + t.window.borderless = false + t.window.resizable = false + t.window.minwidth = 500 + t.window.minheight = 300 + t.window.fullscreen = false + t.window.fullscreentype = "desktop" + t.window.vsync = true + t.window.msaa = 0 + t.window.display = 1 + t.window.highdpi = false + t.window.x = nil + t.window.y = nil + + t.modules.audio = true + t.modules.event = true + t.modules.graphics = true + t.modules.image = true + t.modules.joystick = false + t.modules.keyboard = true + t.modules.math = false + t.modules.mouse = true + t.modules.physics = false + t.modules.sound = true + t.modules.system = true + t.modules.timer = true + t.modules.touch = false + t.modules.video = false + t.modules.window = true + t.modules.thread = false +end diff --git a/2017-uge1/dildo.png b/2017-uge1/dildo.png new file mode 100644 index 0000000..f7ee7ff Binary files /dev/null and b/2017-uge1/dildo.png differ diff --git a/2017-uge1/main.lua b/2017-uge1/main.lua new file mode 100644 index 0000000..4dbf128 --- /dev/null +++ b/2017-uge1/main.lua @@ -0,0 +1,153 @@ +-- State + +local DILDO_COST = 199 + +local DILDOES = { + +} +local LIFE = 100 +local MONEY = 0 + +local WORK_WAIT = 0 + +local function resetGame () + LIFE = 100 + MONEY = 0 + WORK_WAIT = 0 + DILDOES = {} +end + +-------------------------------------------------------------------------------- +-- Colors + +local COLOR_BG = {26/255, 26/255, 26/255} + +-------------------------------------------------------------------------------- +-- DILDO WORK + +local IMG = love.graphics.newImage('dildo.png') + IMG:setFilter('nearest', 'nearest', 1) +local QUADS = {} +for i = 0, 15 do + QUADS[#QUADS+1] = love.graphics.newQuad( (i % 4) * 8, math.floor(i / 4) * 8, 8, 8, IMG:getWidth(), IMG:getHeight() ) +end + +local POSSIBLE_DILDOES = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +} + +local function get_new_dildo () + local l = {} + for _, dildo in ipairs(POSSIBLE_DILDOES) do + l[dildo] = true + end + for dildo, _ in pairs(DILDOES) do + l[dildo] = nil + end + return next(l, nil) +end + +local function has_all_dildoes () + return not get_new_dildo() +end + +local function set_has_dildo (dildo_id) + DILDOES[dildo_id] = true +end + +-------------------------------------------------------------------------------- + +local function can_do_work () + return WORK_WAIT <= 0 +end + +local function do_work () + MONEY = MONEY + 150 + math.floor( 0.3*(100-LIFE)) + LIFE = LIFE - 17 + WORK_WAIT = 5 +end + +local function can_survive () + return MONEY >= 100 and LIFE <= 70 +end + +local function do_survive () + MONEY = MONEY - 100 + LIFE = 100 +end + +local function can_buy_dildo () + return MONEY >= DILDO_COST and not has_all_dildoes() +end + +local function buy_dildo () + MONEY = MONEY - DILDO_COST + local dildo_id = get_new_dildo() + set_has_dildo(dildo_id) +end + +-------------------------------------------------------------------------------- + +local BUTTONS = { + { x = 50, y = 020, width = 100, height = 20, text = 'Work', appear = can_do_work, func = do_work }, + { x = 50, y = 120, width = 100, height = 20, text = 'Survive', appear = can_survive, func = do_survive }, + { x = 50, y = 220, width = 100, height = 20, text = 'Buy Dildo', appear = can_buy_dildo, func = buy_dildo }, +} + +-------------------------------------------------------------------------------- + +function love.load () + resetGame() +end + +function love.update (dt) + WORK_WAIT = WORK_WAIT - dt + LIFE = LIFE - dt * 5 + if LIFE <= 0 then + resetGame() + end +end + +function love.mousepressed (x, y) + for _, button in ipairs(BUTTONS) do + if button.x <= x and x <= button.x+button.width and button.y <= y and y <= button.y+button.height and button.appear() then + button.func(button) + break + end + end +end + +function love.keypressed(key) + if key == 'escape' then + love.event.quit() + end +end + +function love.draw () + love.graphics.setBackgroundColor(COLOR_BG) + + -- Draw buttons + for _, button in ipairs(BUTTONS) do + if button.appear() then + love.graphics.rectangle('line', button.x - 0.5, button.y - 0.5, button.width + 1, button.height + 1) + love.graphics.printf(button.text, button.x, button.y + 5, button.width, 'center') + end + end + + -- Draw survive bar + love.graphics.rectangle('line', 200-0.5, 120-0.5, 101, 21) + love.graphics.rectangle('fill', 200, 120, LIFE, 20) + + -- Draw dollarydooes + love.graphics.print(MONEY..' $', 200, 20) + + -- Draw Didloes + if next(DILDOES, nil) then + --love.graphics.rectangle('line', 200 - 0.5, 220 - 0.5, 3*70+64, 3*70+64) + end + for dildo_id, _ in ipairs(DILDOES) do + local x, y = 200 + 70 * ((dildo_id - 1) % 4), 220 + 70 * math.floor((dildo_id - 1) / 4) + love.graphics.draw( IMG, QUADS[dildo_id], x, y, 0, 4, 4 ) + --love.graphics.rectangle('fill', x, y, 64, 64) + end +end diff --git a/2017-uge2/All_Reverb.mp3 b/2017-uge2/All_Reverb.mp3 new file mode 100644 index 0000000..65a569a Binary files /dev/null and b/2017-uge2/All_Reverb.mp3 differ diff --git a/2017-uge2/All_Star_NoIntro.mp3 b/2017-uge2/All_Star_NoIntro.mp3 new file mode 100644 index 0000000..ab79941 Binary files /dev/null and b/2017-uge2/All_Star_NoIntro.mp3 differ diff --git a/2017-uge2/conf.lua b/2017-uge2/conf.lua new file mode 100644 index 0000000..e357d55 --- /dev/null +++ b/2017-uge2/conf.lua @@ -0,0 +1,42 @@ +function love.conf(t) + t.identity = "allstar" + t.version = "11.1" + t.console = false + t.accelerometerjoystick = false + t.externalstorage = false + t.gammacorrect = false + + t.window.title = "All Star" + t.window.icon = nil--'images/icon.png' + t.window.width = 800 + t.window.height = 600 + t.window.borderless = false + t.window.resizable = false + t.window.minwidth = 800 + t.window.minheight = 600 + t.window.fullscreen = false + t.window.fullscreentype = "desktop" + t.window.vsync = true + t.window.msaa = 0 + t.window.display = 1 + t.window.highdpi = false + t.window.x = nil + t.window.y = nil + + t.modules.audio = true + t.modules.event = true + t.modules.graphics = true + t.modules.image = true + t.modules.joystick = false + t.modules.keyboard = true + t.modules.math = false + t.modules.mouse = true + t.modules.physics = false + t.modules.sound = true + t.modules.system = true + t.modules.timer = true + t.modules.touch = false + t.modules.video = false + t.modules.window = true + t.modules.thread = false +end diff --git a/2017-uge2/main.lua b/2017-uge2/main.lua new file mode 100644 index 0000000..33a20b2 --- /dev/null +++ b/2017-uge2/main.lua @@ -0,0 +1,244 @@ + +-- Colors +local COLOR_WHITE = {1,1,1} +local COLOR_BG = {26/255,26/255,26/255} +local COLOR_PLAYER = {0, 1, 0} + +-- Resources + +local MUSIC_1 = love.audio.newSource('/All_Star_NoIntro.mp3', 'stream') +local MUSIC_2 = love.audio.newSource('/All_Reverb.mp3', 'stream') + +local FONT = love.graphics.newFont('/star_font.ttf', 70) + +-------------------------------------------------------------------------------- + +math.randomseed(os.time()) + +love.graphics.setFont(FONT) + +local plat = require "plat" + +local system = plat.PlatformSystem.new() + system:addPlatform(000, 400, 800, 300) + system:addPlatform(-50, -200000, 50, 400000) + system:addPlatform(800, -200000, 50, 400000) + + system:addPlatform(450, 200, 100, 50) + system:addPlatform(200, 50, 100, 50) + system:addPlatform(450, -50, 100, 50) + system:addPlatform(200, -150, 100, 50) + system:addPlatform(200, -350, 100, 50) + system:addPlatform(100, -550, 100, 50) + system:addPlatform(300, -750, 250, 50) + system:addPlatform(50, -700, 100, 50) + system:addPlatform(550, -950, 150, 50) + system:addPlatform(300, -1100, 100, 50) + system:addPlatform(500, -1300, 100, 50) + system:addPlatform(150, -1400, 100, 50) + system:addPlatform(650, -1500, 100, 50) + system:addPlatform(300, -1550, 100, 50) + system:addPlatform(500, -1700, 100, 50) + system:addPlatform(250, -1850, 100, 50) + system:addPlatform(100, -2050, 50, 250) + system:addPlatform(400, -2200, 50, 200) + system:addPlatform(650, -2200, 50, 200) + system:addPlatform(650, -2500, 50, 200) + system:addPlatform(700, -2200, 100, 50) + system:addPlatform(700, -2450, 50, 50) + system:addPlatform(700, -2450, 50, 50) + system:addPlatform(600, -2450, 50, 50) + system:addPlatform(550, -2400, 50, 50) + system:addPlatform(450, -2700, 150, 50) + system:addPlatform(350, -2750, 150, 50) + system:addPlatform(300, -2900, 50, 150) + system:addPlatform(300, -2950, 100, 50) + system:addPlatform(650, -3250, 50, 200) + system:addPlatform(500, -3150, 150, 50) + system:addPlatform(600, -3400, 150, 50) + system:addPlatform(250, -3300, 150, 50) + system:addPlatform(450, -3700, 50, 300) + system:addPlatform(300, -3550, 150, 50) + system:addPlatform(100, -3500, 50, 100) + system:addPlatform(450, -3750, 150, 50) + system:addPlatform(350, -4100, 50, 250) + system:addPlatform(400, -4000, 150, 100) + system:addPlatform(700, -3900, 50, 150) + system:addPlatform(500, -4250, 300, 50) + system:addPlatform(0, -4450, 350, 50) + system:addPlatform(150, -4650, 50, 50) + system:addPlatform(250, -4850, 50, 50) + system:addPlatform(400, -4850, 50, 50) + system:addPlatform(550, -4850, 50, 50) + system:addPlatform(700, -5050, 50, 50) + system:addPlatform(450, -5250, 50, 50) + system:addPlatform(750, -5450, 50, 50) + system:addPlatform(0, -5500, 50, 50) + system:addPlatform(0, -5650, 50, 150) + system:addPlatform(750, -5800, 50, 150) + system:addPlatform(300, -6100, 200, 50) + system:addPlatform(250, -6050, 300, 50) + system:addPlatform(200, -6000, 400, 50) + system:addPlatform(350, -6150, 100, 50) + system:addPlatform(550, -6350, 200, 50) + system:addPlatform(50, -6350, 200, 50) + system:addPlatform(300, -6550, 200, 50) + system:addPlatform(350, -6750, 50, 50) + system:addPlatform(400, -6950, 50, 50) + system:addPlatform(350, -7150, 50, 50) + system:addPlatform(400, -7300, 50, 50) + system:addPlatform(550, -7500, 150, 50) + system:addPlatform(300, -7500, 150, 50) + system:addPlatform(350, -7850, 50, 250) + system:addPlatform(200, -7750, 150, 50) + system:addPlatform(0, -7650, 100, 50) + system:addPlatform(500, -8000, 200, 50) + system:addPlatform(250, -8200, 200, 50) + system:addPlatform(500, -8400, 50, 50) + system:addPlatform(200, -8600, 50, 50) + system:addPlatform(550, -8800, 50, 50) + system:addPlatform(350, -8950, 50, 50) + system:addPlatform(300, -9150, 150, 50) + system:addPlatform(300, -9350, 150, 50) + system:addPlatform(300, -9550, 150, 50) + system:addPlatform(150, -9750, 150, 50) + system:addPlatform(450, -9750, 150, 50) + system:addPlatform(300, -9950, 150, 50) + system:addPlatform(400, -10150, 50, 50) + system:addPlatform(300, -10350, 50, 50) + system:addPlatform(400, -10550, 50, 50) + system:addPlatform(450, -10750, 100, 50) + system:addPlatform(200, -10750, 100, 50) + system:addPlatform((800-250)/2, -10950, 250, 50) + + +local BEST_SCORE = math.huge +local PREVIOUS_SCORE = {} +local START_OF_GAME, END_OF_GAME = 0, 0 + +local player = system:addPlayer((800-50)/2, 350, 50, 50, {jump = 'up', left = 'left', right = 'right'}) + player.color = COLOR_PLAYER + +local stars = {} +for i = 1, 1000 do + stars[i] = { math.random() * 800, math.random() * 600 * 4 - 1800 } +end + +local function set_score (score) + PREVIOUS_SCORE = score + local SCORE_L = tostring(math.floor(BEST_SCORE*100)/100) + PREVIOUS_SCORE[#PREVIOUS_SCORE+1] = ' ' + for i = 1, #SCORE_L do PREVIOUS_SCORE[#PREVIOUS_SCORE+1] = SCORE_L:sub(i,i) end + for i = 1, #score do + score[i] = { score[i] } + score[i].x = math.random() * 10 - 5 + score[i].y = math.random() * 10 - 5 + score[i].r = math.random() * math.pi/2 - math.pi/4 + end +end + + +-------------------------------------------------------------------------------- + +local reset_game = nil + +local goto_highscore = function () + local new_score = END_OF_GAME - START_OF_GAME + local old_draw, text = love.draw, {} + if new_score < BEST_SCORE then + BEST_SCORE = new_score + love.keypressed = function (key) + if key == 'escape' then + love.event.quit() + elseif key == 'return' and #text >= 1 then + set_score(text) + reset_game() + elseif key == 'backspace' then + text[#text] = nil + elseif #key == 1 and #text < 3 then + text[#text+1] = key:upper() + end + end + love.draw = function () old_draw(); love.graphics.printf('New High Score!\nEnter name:\n'..table.concat(text), 0, 200, 800, 'center') end + else + love.keypressed = function () + if key == 'escape' then + love.event.quit() + else + reset_game() + end + end + love.draw = function () old_draw(); love.graphics.printf(':(', 0, 200, 800, 'center') end + end + + love.update = function () end + player.movable = false +end + +-------------------------------------------------------------------------------- + +local update_game = function (dt) + system:update(dt) + + if player.y < -800 and not MUSIC_1:isPlaying() then MUSIC_1:play(); MUSIC_2:play(); START_OF_GAME = love.timer.getTime(); print("start") end + if player.y < -10950 then player.dy = player.dy - 1000 * dt end + if player.y < -10950 and not END_OF_GAME then END_OF_GAME = love.timer.getTime() end + if player.y < -200000 then goto_highscore() end + + if love.keyboard.isDown('f2') then player.dy = player.dy - 1000 * dt end + + COLOR_PLAYER[1], COLOR_PLAYER[3] = (player.y+800)/-100000, (player.y+800)/-100000 + MUSIC_1:setVolume((player.y+800)/-12000) + MUSIC_2:setVolume((1-(player.y+800)/-12000)) +end + +local draw_previous_score = function (bx, by, inconsitency) + for i, b in ipairs(PREVIOUS_SCORE) do + love.graphics.print(b[1], b.x * inconsitency + bx + (i-1) * 80, by + b.y * inconsitency, b.r * inconsitency ) + end +end + +local draw_game = function () + love.graphics.setBackgroundColor(COLOR_BG) + + -- Draw score + love.graphics.setColor(COLOR_WHITE) + draw_previous_score(100, 200*player.y/-10000 + 200, player.y/-3000) + + -- Draw Stars + love.graphics.setColor(COLOR_WHITE) + if player.y < 10000 then + for i = 1, #stars do + love.graphics.points(stars[i][1], - 600 + stars[i][2] - player.y/100) + end + end + + -- Draw System + system:draw(player.x, player.y - (800-50)/2 - ((player.y < -11000) and (player.y+11000)/400 or 0)) +end + +function reset_game ( set_score ) + player.x, player.y = (800-50)/2, 250 + --player.x, player.y = (800-50)/2, -10000 + player.dy, player.dx = 0, 0 + player.movable = true + MUSIC_1:stop(); MUSIC_2:stop() + + love.update = update_game + love.draw = draw_game + love.keypressed = function (key) + if key == 'escape' then + love.event.quit() + elseif key == 'r' then + reset_game() + end + end + + END_OF_GAME = nil +end + + + +-------------------------------------------------------------------------------- + +love.load = reset_game diff --git a/2017-uge2/plat.lua b/2017-uge2/plat.lua new file mode 100644 index 0000000..3691152 --- /dev/null +++ b/2017-uge2/plat.lua @@ -0,0 +1,131 @@ + +local DEFAULT_PLATFORM_COLOR = {255,255,255} + +local GRAVITY_CONSTANT = 300 +local AIR_RES_CONSTANT = 1 +local VERY_SMALL_NUM = 0.01 + +local function platform_below_platform (platform, system) + return system:isPointOccupied(platform.x, platform.y + platform.h + VERY_SMALL_NUM, platform) or system:isPointOccupied(platform.x + platform.w, platform.y + platform.h + VERY_SMALL_NUM, platform) +end + +-------------------------------------------------------------------------------- + +local PlayerController = {} + PlayerController.__index = PlayerController + +function PlayerController.new (target, controls) + assert(controls.jump and controls.left and controls.right ) + local self = setmetatable({}, PlayerController) + self.target = target + self.controls = controls + return self +end + +function PlayerController:update (dt, system) + local not_jumping = platform_below_platform(self.target, system) + if love.keyboard.isDown( self.controls.jump ) then + self.target.dy = self.target.dy + (not_jumping and -260 or (-280 * dt)) + end + if love.keyboard.isDown( self.controls.left ) then + self.target.dx = self.target.dx - 15 + end + if love.keyboard.isDown( self.controls.right ) then + self.target.dx = self.target.dx + 15 + end +end + +-------------------------------------------------------------------------------- + +local Platform = {} + Platform.__index = Platform + +function Platform.new (t) + local self = setmetatable(t, Platform) + assert(self.x and self.y and self.w and self.y) + self.color = self.color or DEFAULT_PLATFORM_COLOR + return self +end + +-------------------------------------------------------------------------------- + +local PlatformSystem = {} + PlatformSystem.__index = PlatformSystem + +function PlatformSystem.new () + local self = setmetatable({}, PlatformSystem) + self.platforms = {} + self.controllers = {} + return self +end + +function PlatformSystem:addPlatform (x, y, w, h) + self.platforms[#self.platforms + 1] = Platform.new { + x = x, y = y, w = w, h = h + } +end + +function PlatformSystem:addPlayer ( x, y, w, h, controls ) + local platform = Platform.new { + x = x, y = y, w = w, h = h, + movable = true, dx = 0, dy = 0 + } + self.platforms[#self.platforms + 1] = platform + self.controllers[#self.controllers + 1] = PlayerController.new(platform, controls) + return platform +end + +function PlatformSystem:isPointOccupied (x, y, except) + for _, platform in ipairs(self.platforms) do + if platform.x <= x and x <= platform.x + platform.w and platform.y <= y and y <= platform.y + platform.h and platform ~= except then + return platform + end + end + return false +end + +function PlatformSystem:updatePlatform (platform, dt) + local ndx, ndy = platform.dx * (1 - AIR_RES_CONSTANT * dt), platform.dy + GRAVITY_CONSTANT * dt + local nx, ny = platform.x + ndx * dt, platform.y + ndy * dt + if true then + if not self:isPointOccupied(platform.x, ny, platform) and not self:isPointOccupied(platform.x + platform.w, ny, platform) and not self:isPointOccupied(platform.x, ny + platform.h, platform) and not self:isPointOccupied(platform.x + platform.w, ny + platform.h, platform) then + platform.y, platform.dy = ny, ndy + else + ndx = ndx * (1 - AIR_RES_CONSTANT * dt) + platform.dy = 0 + end + if not self:isPointOccupied(nx, platform.y, platform) and not self:isPointOccupied(nx + platform.w, platform.y, platform) and not self:isPointOccupied(nx, platform.y + platform.h, platform) and not self:isPointOccupied(nx + platform.w, platform.y + platform.h, platform) then + platform.x, platform.dx = nx, ndx + else + platform.dx = 0 + end + end +end + +function PlatformSystem:update (dt) + -- Update movable platforms + for _, platform in ipairs(self.platforms) do + if platform.movable then + platform.moved = self:updatePlatform(platform, dt) or self:updatePlatform(platform, dt * 0.5) or self:updatePlatform(platform, dt * 0.25) + end + end + -- Update controllers + for _, controller in ipairs(self.controllers) do + controller:update(dt, self) + end +end + +function PlatformSystem:draw (dt, offset_x, offset_y) + local offset_x, offset_y = offset_x or 0, offset_y or 0 + for _, platform in ipairs(self.platforms) do + love.graphics.setColor(platform.color) + love.graphics.rectangle('fill', platform.x - offset_y, platform.y - offset_x, platform.w, platform.h) + end +end + +-------------------------------------------------------------------------------- + +return { + PlatformSystem = PlatformSystem, + Player = Player, +} diff --git a/2017-uge2/star_font.ttf b/2017-uge2/star_font.ttf new file mode 100644 index 0000000..f957380 Binary files /dev/null and b/2017-uge2/star_font.ttf differ diff --git a/2017-uge3/All_Star_NoIntro.mp3 b/2017-uge3/All_Star_NoIntro.mp3 new file mode 100644 index 0000000..ab79941 Binary files /dev/null and b/2017-uge3/All_Star_NoIntro.mp3 differ diff --git a/2017-uge3/all_stars.love b/2017-uge3/all_stars.love new file mode 100644 index 0000000..3ead24c Binary files /dev/null and b/2017-uge3/all_stars.love differ diff --git a/2017-uge3/color_tools.lua b/2017-uge3/color_tools.lua new file mode 100644 index 0000000..92388ce --- /dev/null +++ b/2017-uge3/color_tools.lua @@ -0,0 +1,120 @@ +--[[ + This is a collection of functions for performing color operations, + designed to work with the LÖVE game engine. + + See `README_COLOR_TOOLS.md` for full documentation. + + LICENSE is BEER-WARE. +]] + + +local color_tools = {} + +-------------------------------------------------------------------------------- +-- Conversion + +color_tools.rgb_to_hsl = function (color) + local r = (color[1] == 255 and 1 or color[1]/256) + local g = (color[2] == 255 and 1 or color[2]/256) + local b = (color[3] == 255 and 1 or color[3]/256) + + local max, min = math.max(r, g, b), math.min(r, g, b) + local h, s, l = (max+min)/2, (max+min)/2, (max+min)/2 + if min == max then + return {0, 0, l} + end + + local d = max - min + s = l > 0.5 and d / (2 - max - min) or d / (max + min) + if max == r then + h = ((g - b) / d + (g < b and 6 or 0)) / 6 + elseif max == g then + h = ((b - r) / d + 2) / 6 + elseif max == b then + h = ((r - g) / d + 4) / 6 + end + + return {h, s, l} +end + +local function hue_to_rgb (p, q, t) + if t < 0 then + t = t + 1 + end + if t > 1 then + t = t - 1 + end + if t < 1/6 then + return p + (q - p) * 6 * t + elseif t < 1/2 then + return q + elseif t < 2/3 then + return p + (q - p) * 6 * (2/3 - t) + end + return p +end + +color_tools.hsl_to_rgb = function (color) + local h, s, l = color[1], color[2], color[3] + local r, g, b + if s == 0 then + r, g, b = l, l, l + else + local q = l < 0.5 and l * (1+s) or l+s - l*s + local p = 2 * l - q + r, g, b = hue_to_rgb(p,q,h+1/3), hue_to_rgb(p,q,h), hue_to_rgb(p,q,h-1/3) + end + r, g, b = math.floor(math.min(255,r*256)+0.5), math.floor(math.min(255,g*256)+0.5), math.floor(math.min(255,b*256)+0.5) + return { r, g, b } +end + +-------------------------------------------------------------------------------- +-- Interpolation + +color_tools.interpolate_rgb = function (c1, c2, t) + return { c1[1]+(c2[1]-c1[1])*t, c1[2]+(c2[2]-c1[2])*t, c1[3]+(c2[3]-c1[3])*t } +end + +local function angle_delta (a1, a2) + local r1, r2 = (a1-a2)%1, (a2-a1)%1 + return r1 < r2 and -r1 or r2 +end + +color_tools.interpolate_hsl = function (c1, c2, t) + local h = c1[1] + angle_delta(c1[1], c2[1]) * t + local s = c1[2] + (c2[2] - c1[2]) * t + local l = c1[3] + (c2[3] - c1[3]) * t + return { h, s, l } +end + +color_tools.interpolate_rgb_by_way_of_hsl = function (c1, c2, t) + local c1_hsl = color_tools.rgb_to_hsl(c1) + local c2_hsl = color_tools.rgb_to_hsl(c2) + local c3_hsl = color_tools.interpolate_hsl(c1_hsl, c2_hsl, t) + return color_tools.hsl_to_rgb(c3_hsl) +end + +-------------------------------------------------------------------------------- +-- Inversion + +color_tools.invert_rgb = function (color) + return { 255 - color[1], 255 - color[2], 255 - color[3] } +end + +-------------------------------------------------------------------------------- +-- Parsing + +color_tools.parse_rgb = function (str) + local r, g, b + local o = (str:sub(1,1) == '#') and 1 or 0 -- Offset + if #str == 3 or #str == 4 then + r, g, b = str:sub(o+1,o+1):rep(2), str:sub(o+2,o+2):rep(2), str:sub(o+3,o+3):rep(2) + else + r, g, b = str:sub(o+1,o+2), str:sub(o+3,o+4), str:sub(o+5,o+6) + end + return {tonumber('0x'..r), tonumber('0x'..g), tonumber('0x'..b)} +end + +-------------------------------------------------------------------------------- + +return color_tools diff --git a/2017-uge3/conf.lua b/2017-uge3/conf.lua new file mode 100644 index 0000000..0150118 --- /dev/null +++ b/2017-uge3/conf.lua @@ -0,0 +1,42 @@ +function love.conf(t) + t.identity = "all_stars" + t.version = "11.1" + t.console = false + t.accelerometerjoystick = false + t.externalstorage = false + t.gammacorrect = false + + t.window.title = "All Stars" + t.window.icon = nil--'images/icon.png' + t.window.width = 800 + t.window.height = 600 + t.window.borderless = false + t.window.resizable = false + t.window.minwidth = 800 + t.window.minheight = 600 + t.window.fullscreen = false + t.window.fullscreentype = "desktop" + t.window.vsync = true + t.window.msaa = 0 + t.window.display = 1 + t.window.highdpi = false + t.window.x = nil + t.window.y = nil + + t.modules.audio = true + t.modules.event = true + t.modules.graphics = true + t.modules.image = true + t.modules.joystick = false + t.modules.keyboard = true + t.modules.math = false + t.modules.mouse = true + t.modules.physics = false + t.modules.sound = true + t.modules.system = true + t.modules.timer = true + t.modules.touch = false + t.modules.video = false + t.modules.window = true + t.modules.thread = false +end diff --git a/2017-uge3/main.lua b/2017-uge3/main.lua new file mode 100644 index 0000000..d5a1612 --- /dev/null +++ b/2017-uge3/main.lua @@ -0,0 +1,195 @@ + +-------------------------------------------------------------------------------- +-- Colors + +local COLOR_PLAYER_1 = {200/255, 255/255, 200/255} +local COLOR_PLAYER_2 = {200/255, 200/255, 255/255} +local COLOR_WHITE = {255/255,255/255,255/255} +local COLOR_LOWER = {230/255, 230/255, 255/255} +local COLOR_BG = {26/255,26/255,26/255} + + +-------------------------------------------------------------------------------- + + +WINDOW_WIDTH, WINDOW_HEIGTH = 800, 600 + +local SOMEBODY_ONCE = false +local SOMEBODY_MUSC = love.audio.newSource('/All_Star_NoIntro.mp3', 'stream') + SOMEBODY_MUSC:setLooping(true) + SOMEBODY_MUSC:play() +local SOMEBODIES = {} + +local plat = require "plat" +local color = require "color_tools" +local FONT = love.graphics.newFont('star_font.ttf', 60) +love.graphics.setFont(FONT) + +local system, player_1, player_2, reset_timer + +-- Stars + +local Stars = {} + +function Stars:generate (nr_stars) + for i = 1, 200 do + self[#self+1] = math.random() * WINDOW_WIDTH + self[#self+1] = math.random() ^ 2 * WINDOW_HEIGTH + end +end + +function Stars:update (dt, scores) + local star_speed = dt * (scores[2] - scores[1]) + if star_speed == 0 then return end + for i = 1, #self, 2 do + self[i] = (self[i] + star_speed * self[i+1]/WINDOW_HEIGTH) % WINDOW_WIDTH + end +end + +function Stars:draw () + love.graphics.points(self) +end + +-- Scores + +local SCORES = { 0, 0 } + +local function draw_scores () + love.graphics.printf(SCORES[1], 50, 600 - 80, 700, 'left') + love.graphics.printf(SCORES[2], 50, 600 - 80, 700, 'right') +end + +function reset_game () + system = plat.PlatformSystem.new() + system:addPlatform(50, 400, 100, 50) + system:addPlatform(800-50-100, 400, 100, 50) + system:addPlatform(300, 200, 200, 50) + local lower = system:addPlatform(300, 550, 200, 50) + lower.resistance = -0.4 + lower.color = COLOR_LOWER + + player_1 = system:addPlayer(100, 50, 50, 50, {jump = 'w', left = 'a', right = 'd'}) + player_1.start_t = 0 + player_1.controller.last_dir = 1 + + player_2 = system:addPlayer(800 - 100 - 50, 50, 50, 50, {jump = 'up', left = 'left', right = 'right'}) + player_2.start_t = 0 + player_2.controller.last_dir = -1 + + reset_timer = nil + + if SOMEBODY_ONCE then + local somb = SOMEBODY_MUSC:clone() + somb:play() + somb:setLooping(true) + SOMEBODIES[#SOMEBODIES+1] = somb + end +end + +local function is_player_outside_bounds (player) + return player.x + player.w < -100 or 900 < player.x or player.y > 700 +end + +-------------------------------------------------------------------------------- + +function love.load () + math.randomseed(os.time()) + Stars:generate(1000) + reset_game() +end + +local PLAYER_1_SHOOT = 's' +local PLAYER_2_SHOOT = 'down' + +function love.update (dt) + Stars:update(dt, SCORES) + system:update(dt) + + -- Update color + player_1.color = color.interpolate_rgb(COLOR_PLAYER_1, COLOR_WHITE, love.keyboard.isDown(PLAYER_1_SHOOT) and math.max(0, math.min(1, love.timer.getTime() - player_1.start_t)) or 0) + player_2.color = color.interpolate_rgb(COLOR_PLAYER_2, COLOR_WHITE, love.keyboard.isDown(PLAYER_2_SHOOT) and math.max(0, math.min(1, love.timer.getTime() - player_2.start_t)) or 0) + + if reset_timer then + reset_timer = reset_timer - dt + if reset_timer < 0 then + reset_game() + end + end + if is_player_outside_bounds(player_1) and player_1.movable then + SCORES[2] = SCORES[2] + 1 + player_1.movable = false + reset_timer = 1 + end + if is_player_outside_bounds(player_2) and player_2.movable then + SCORES[1] = SCORES[1] + 1 + player_2.movable = false + reset_timer = reset_timer or 1 + end +end + +local function shoot_bullet_at_player (player) + -- TODO: Add Timeout when shooting bullets shorter than 0.2 + local time = love.timer.getTime() - player.start_t + local size = math.max(math.min( 1, time ), 0.2) + local dx = player.controller.last_dir * (1.2 - size) * 600 + player.dx + if dx ~= 0 then + local bw, bh = math.floor(40*size + 0.5), math.floor(20*size + 0.5) + local bx, by = player.x + player.w/2 - bw/2, player.y + player.h/2 - bh/2 + system:addBullet(bx, by, bw, bh, dx, player) + end +end + +function love.keyreleased (key) + if key == PLAYER_1_SHOOT then shoot_bullet_at_player(player_1) end + if key == PLAYER_2_SHOOT then shoot_bullet_at_player(player_2) end +end + +function love.keypressed (key) + if key == 'escape' then + love.event.quit() + end + if key == 's' and player_1.start_t < love.timer.getTime() then + player_1.start_t = love.timer.getTime() + end + if key == 'down' and player_2.start_t < love.timer.getTime() then + player_2.start_t = love.timer.getTime() + end + if key == 'f2' then + SOMEBODY_MUSC:stop() + end + if key == 'f1' then + SOMEBODY_ONCE = not SOMEBODY_ONCE + if not SOMEBODY_ONCE then + for _, track in ipairs(SOMEBODIES) do + track:stop() + end + SOMEBODIES = {} + end + end +end + +function love.draw () + love.graphics.setBackgroundColor(COLOR_BG) + love.graphics.setColor(COLOR_WHITE) + + -- Draw score + draw_scores() + + -- Draw Stars + Stars:draw() + + -- Draw System + system:draw() + + if is_player_outside_bounds(player_1) then + love.graphics.setColor(COLOR_PLAYER_1) + love.graphics.rectangle('fill', player_1.x-4000, player_1.y, 8000, 50) + love.graphics.rectangle('fill', player_1.x, player_1.y-4000, 50, 8000) + end + if is_player_outside_bounds(player_2) then + love.graphics.setColor(COLOR_PLAYER_2) + love.graphics.rectangle('fill', player_2.x-4000, player_2.y, 8000, 50) + love.graphics.rectangle('fill', player_2.x, player_2.y-4000, 50, 8000) + end + +end diff --git a/2017-uge3/plat.lua b/2017-uge3/plat.lua new file mode 100644 index 0000000..9233155 --- /dev/null +++ b/2017-uge3/plat.lua @@ -0,0 +1,241 @@ + +local DEFAULT_PLATFORM_COLOR = {255,255,255} + +local GRAVITY_CONSTANT = 700 +local AIR_RES_CONSTANT = 0.5 +local DEFAULT_PLATFORM_RES = 0.7 +local VERY_SMALL_NUM = 0.01 +local PLAYER_MOVE_SPEED = 20 +local PUSH_RESISTANCE = math.huge -- Should be >=2, set to math.huge to disable pushing +local BOUNCE_CONSTANT_X = 0 +local BOUNCE_CONSTANT_Y = 0 + +local function platform_below_platform (platform, system) + local tmp + tmp, platform.collided_on_y_axis = platform.collided_on_y_axis, false + return tmp +end + +-------------------------------------------------------------------------------- + +local BulletController +local PlayerController = {} + PlayerController.__index = PlayerController + +function PlayerController.new (target, controls) + assert(controls.jump and controls.left and controls.right ) + local self = setmetatable({}, PlayerController) + self.target = target + self.controls = controls + self.last_dir = 0 + return self +end + +function PlayerController:update (dt, system) + local not_jumping = platform_below_platform(self.target, system) + if love.keyboard.isDown( self.controls.jump ) then + self.target.dy = self.target.dy + (not_jumping and -400 or (-400 * dt)) + end + if love.keyboard.isDown( self.controls.left ) then + self.target.dx = self.target.dx - PLAYER_MOVE_SPEED + self.last_dir = -1 + end + if love.keyboard.isDown( self.controls.right ) then + self.target.dx = self.target.dx + PLAYER_MOVE_SPEED + self.last_dir = 1 + end +end + +function PlayerController:canCollide (platform, collided_with) + return platform ~= collided_with + and (not collided_with.controller or collided_with.controller.__index ~= BulletController) +end + +function PlayerController:onCollide (platform, collided_with, system) +end + +-------------------------------------------------------------------------------- + + BulletController = {} + BulletController.__index = BulletController + +function BulletController.new (target, owner) + local self = setmetatable({}, BulletController) + self.target = target + self.owner = owner + self.owner_im = 0.5 + return self +end + +function BulletController:update (dt, system) + self.target.x = (self.target.x + self.target.w + (WINDOW_WIDTH + self.target.w)) % (WINDOW_WIDTH + self.target.w) - self.target.w + self.owner_im = self.owner_im - dt +end + +function BulletController:canCollide (platform, collided_with) + return collided_with ~= platform and (collided_with ~= self.owner or self.owner_im <= 0) and (collided_with.controller and collided_with.controller.__index ~= BulletController) +end + +function BulletController:onCollide (platform, collided_with, system) + local dir = platform.dx / math.abs(platform.dx) + local force = dir * ((platform.w / (40*0.2) )^1.5) * (40*0.2) + local ndx, ndy = collided_with.dx + force * 15, collided_with.dy + -platform.h * 12 + + if ndx == ndx and ndy == ndy then -- Prevent nan values + collided_with.dx, collided_with.dy = ndx, ndy + system:removeController(self) + system:removePlatform(self.target) + end +end + +-------------------------------------------------------------------------------- + +local Platform = {} + Platform.__index = Platform + +function Platform.new (t) + local self = setmetatable(t, Platform) + assert(self.x and self.y and self.w and self.y) + self.color = self.color or DEFAULT_PLATFORM_COLOR + self.resistance = DEFAULT_PLATFORM_RES + return self +end + +-------------------------------------------------------------------------------- + +local PlatformSystem = {} + PlatformSystem.__index = PlatformSystem + +function PlatformSystem.new () + local self = setmetatable({}, PlatformSystem) + self.platforms = {} + self.controllers = {} + return self +end + +function PlatformSystem:addPlatform (x, y, w, h) + local platform = Platform.new { + x = x, y = y, w = w, h = h + } + self.platforms[platform] = true + return platform +end + +function PlatformSystem:removeController( controller ) + self.controllers[controller] = nil +end + +function PlatformSystem:removePlatform( platform ) + self.platforms[platform] = nil +end + +function PlatformSystem:addPlayer ( x, y, w, h, controls ) + local platform = Platform.new { + x = x, y = y, w = w, h = h, + movable = true, dx = 0, dy = 0 + } + platform.controller = PlayerController.new(platform, controls) + self.platforms[platform] = true + self.controllers[platform.controller] = true + return platform +end + +function PlatformSystem:addBullet (x, y, w, h, dx, owner) + local platform = Platform.new { + x = x, y = y, w = w, h = h, + movable = true, dx = dx, dy = 0, constant = true + } + platform.controller = BulletController.new(platform, owner) + self.platforms[platform] = true + self.controllers[platform.controller] = true + return platform +end + +function PlatformSystem:isPointOccupied (x, y, who_is_asking) + for platform, _ in pairs(self.platforms) do + if platform.x <= x and x <= platform.x + platform.w and platform.y <= y and y <= platform.y + platform.h and who_is_asking.controller:canCollide(who_is_asking, platform) then + return platform + end + end + return nil +end + +local function is_movement_on_collision_course (platform, nx, ny, platforms, func) + for other in pairs(platforms) do + if func(platform, nx, ny, other) then + return other + end + end +end + +local function check_move_x (platform, nx, ny, other) + return platform ~= other and nx < other.x + other.w and other.x < nx + platform.w and other.y < platform.y + platform.h and platform.y < other.y + other.h and platform.controller:canCollide(platform, other) +end + +local function check_move_y (platform, nx, ny, other) + return platform ~= other and ny < other.y + other.h and other.y < ny + platform.h and other.x < platform.x + platform.w and platform.x < other.x + other.w and platform.controller:canCollide(platform, other) +end + +function PlatformSystem:updatePlatform (platform, dt) + local ndx, ndy = platform.dx * (platform.constant and 1 or (1 - AIR_RES_CONSTANT) ^ dt), platform.dy + (platform.constant and 0 or (GRAVITY_CONSTANT * dt)) + local nx, ny = platform.x + ndx * dt, platform.y + ndy * dt + + -- Moving down + do + local collided_with = is_movement_on_collision_course(platform, nx, ny, self.platforms, check_move_y) + if not collided_with then + platform.y, platform.dy = ny, ndy + else + platform.collided_on_y_axis = collided_with and (ndy >= 0) + platform.controller:onCollide(platform, collided_with, self) + ndx = ndx * (platform.constant and 1 or (1 - collided_with.resistance) ^ dt) + platform.y = collided_with.y + ( (ndy >= 0) and -platform.h or collided_with.h) + platform.dy = -platform.dy * BOUNCE_CONSTANT_Y + end + end + + -- Moving on x axis + do + local collided_with = is_movement_on_collision_course(platform, nx, ny, self.platforms, check_move_x) + if not collided_with then + platform.x, platform.dx = nx, ndx + else + platform.controller:onCollide(platform, collided_with, self) + platform.x = collided_with.x + ( (ndx >= 0) and -platform.w or collided_with.w) + if platform.movable and collided_with.movable and self.platforms[platform] and PUSH_RESISTANCE ~= math.huge then + platform.dx, collided_with.dx = (platform.dx + collided_with.dx)/PUSH_RESISTANCE, (platform.dx + collided_with.dx)/PUSH_RESISTANCE + elseif BOUNCE_CONSTANT_X == 0 then + platform.dx = 0 + end + platform.dx = -platform.dx * BOUNCE_CONSTANT_X + end + end +end + +function PlatformSystem:update (dt) + -- Update movable platforms + for platform, _ in pairs(self.platforms) do + if platform.movable then + self:updatePlatform(platform, dt) + end + end + -- Update controllers + for controller, _ in pairs(self.controllers) do + controller:update(dt, self) + end +end + +function PlatformSystem:draw (dt, offset_x, offset_y) + local offset_x, offset_y = offset_x or 0, offset_y or 0 + for platform, _ in pairs(self.platforms) do + love.graphics.setColor(platform.color) + love.graphics.rectangle('fill', platform.x - offset_y, platform.y - offset_x, platform.w, platform.h) + end +end + +-------------------------------------------------------------------------------- + +return { + PlatformSystem = PlatformSystem, + Player = Player, +} diff --git a/2017-uge3/star_font.ttf b/2017-uge3/star_font.ttf new file mode 100644 index 0000000..f957380 Binary files /dev/null and b/2017-uge3/star_font.ttf differ diff --git a/2017-uge4/conf.lua b/2017-uge4/conf.lua new file mode 100644 index 0000000..9710bc4 --- /dev/null +++ b/2017-uge4/conf.lua @@ -0,0 +1,42 @@ +function love.conf(t) + t.identity = "En Forelæsning" + t.version = "11.1" + t.console = false + t.accelerometerjoystick = false + t.externalstorage = false + t.gammacorrect = false + + t.window.title = "En Forelæsning" + t.window.icon = nil--'images/icon.png' + t.window.width = 800 + t.window.height = 400 + t.window.borderless = false + t.window.resizable = false + t.window.minwidth = 800 + t.window.minheight = 400 + t.window.fullscreen = false + t.window.fullscreentype = "desktop" + t.window.vsync = true + t.window.msaa = 0 + t.window.display = 1 + t.window.highdpi = false + t.window.x = nil + t.window.y = nil + + t.modules.audio = true + t.modules.event = true + t.modules.graphics = true + t.modules.image = true + t.modules.joystick = false + t.modules.keyboard = true + t.modules.math = false + t.modules.mouse = true + t.modules.physics = false + t.modules.sound = true + t.modules.system = true + t.modules.timer = true + t.modules.touch = false + t.modules.video = false + t.modules.window = true + t.modules.thread = false +end diff --git a/2017-uge4/en_forelaesning.png b/2017-uge4/en_forelaesning.png new file mode 100644 index 0000000..b8ceafb Binary files /dev/null and b/2017-uge4/en_forelaesning.png differ diff --git a/2017-uge4/main.lua b/2017-uge4/main.lua new file mode 100644 index 0000000..e13fcb5 --- /dev/null +++ b/2017-uge4/main.lua @@ -0,0 +1,267 @@ + +math.randomseed(os.time()) +local WINDOW_WIDTH, WINDOW_HEIGHT = 800, 400 + +local TALK_FONT = love.graphics.newFont(18) +local OPTIONS_FONT = love.graphics.newFont(10) + +local markov = require "markov" + +--- Colors + +local COLOR_WHITE = {1,1,1} +local COLOR_BLACK = {0,0,0} + +local COLOR_CLOCK_FRAME = {200/255,200/255,200/255,} +local COLOR_CLOCK_INNER = COLOR_WHITE +local COLOR_CLOCK_HAND = {50/255,50/255,50/255} + +local COLOR_BOARDS = {(240 + math.random() * 10)/255, (200 + math.random() * 10)/255, (120 + math.random() * 30)/255} + +local COLOR_PROJECTOR_CONTENTS = {82/255, 106/255, 108/255, 30/255} +local COLOR_WHITEBOARD = {200/255, 200/255, 200/255} +local COLOR_BOLTS = {149/255, 141/255, 128/255} +local COLOR_CRACKS = {28/255, 19/255, 20/255} +local COLOR_BEAMS = {127/255, 94/255, 61/255} +local COLOR_PROJECTOR_AREA = {210/25/2555, 210/255, 210/255} +local COLOR_WHITEBOARD_BORDERS = {149/255, 141/255, 128/255} + +local COLOR_HAIR = {59/255, 37/255, 24/255} +local COLOR_SUIT = {87/255, 107/255, 56/255} +local COLOR_HEAD = {163/255, 118/255, 100/255} + +-------------------------------------------------------------------------------- +-- Background + + +local function randomize_projector_color () + for i = 1, 3 do + COLOR_PROJECTOR_CONTENTS[i] = (math.random(80) + 100)/255 + end +end + +local function draw_clock (x, y, r, t) + love.graphics.setColor(COLOR_CLOCK_FRAME) + love.graphics.circle('fill', x, y, r) + love.graphics.setColor(COLOR_CLOCK_INNER) + love.graphics.circle('fill', x, y, r - 2) + love.graphics.setColor(COLOR_CLOCK_HAND) + love.graphics.setLineWidth(3) + love.graphics.line(x, y, x + math.cos(t * (2*math.pi) / 42000 - math.pi / 2) * r * 0.4, y + math.sin(t * (2*math.pi) / 42000 - math.pi / 2) * r * 0.4) + love.graphics.setLineWidth(2) + love.graphics.line(x, y, x + math.cos(t * (2*math.pi) / 3600 - math.pi / 2) * r * 0.6, y + math.sin(t * (2*math.pi) / 3600 - math.pi / 2) * r * 0.6) + love.graphics.setLineWidth(1) + love.graphics.line(x, y, x + math.cos(math.floor(t) * (2*math.pi) / 60 - math.pi / 2) * r * 0.8, y + math.sin(math.floor(t) * (2*math.pi) / 60 - math.pi / 2) * r * 0.8) +end + +local function draw_background () + + -- FLOOR -- + love.graphics.setColor(COLOR_BOARDS) + love.graphics.rectangle('fill',0, 0, WINDOW_WIDTH, WINDOW_HEIGHT) + + -- TODO + + -- WALL -- + + -- Color of cracks + love.graphics.setColor(COLOR_CRACKS) + love.graphics.rectangle('fill',0, 0, WINDOW_WIDTH, 250 + 10) + + -- "Beams" + love.graphics.setColor(COLOR_BEAMS) + for xi = 0.25, 100 do + love.graphics.rectangle('fill',xi*8, 0, 4, 250) + end + -- "Bolts" + love.graphics.setColor(COLOR_BOLTS) + for xi = 0.25, 100 do + for yi = 0.7, 5 do + love.graphics.rectangle('fill',xi*8+1, yi*50, 2, 2) + end + end + + -- Whiteboards and projector area -- + + -- Whiteboard + love.graphics.setColor(COLOR_WHITEBOARD) + love.graphics.rectangle('fill', 100, 100, WINDOW_WIDTH - 100*2, 100) + -- Projector Area + love.graphics.setColor(COLOR_PROJECTOR_AREA) + love.graphics.rectangle('fill', 100, 0, WINDOW_WIDTH - 100*2, 100) + + -- Whiteboard and projector borders + love.graphics.setColor(COLOR_WHITEBOARD_BORDERS) + love.graphics.rectangle('fill', (WINDOW_WIDTH-2)/2, 100, 2, 100) + love.graphics.rectangle('fill', 100, 100, WINDOW_WIDTH - 100*2, 2) + love.graphics.rectangle('fill', 100, 100+100-2, WINDOW_WIDTH - 100*2, 2) + love.graphics.rectangle('fill', 100, 0, 2, 100+100) + love.graphics.rectangle('fill', 100+WINDOW_WIDTH-100*2-2, 0, 2, 100+100) + + -- Projector contents + love.graphics.setColor(COLOR_PROJECTOR_CONTENTS) + for i = 5, 20, 2 do + love.graphics.rectangle('fill', (100+i), 0, WINDOW_WIDTH - (100+i)*2, 100 - i) + end + + + -- OTHER PEOPLE -- + + -- TODO + + -- FOREGROUND + + -- TODO + -- TEMP: + love.graphics.setColor(COLOR_WHITE) + love.graphics.rectangle('fill',0, 400, WINDOW_WIDTH, WINDOW_HEIGHT) +end + + +-------------------------------------------------------------------------------- +-- Teacher + +local TEACHER = { x = WINDOW_WIDTH/2, y = WINDOW_HEIGHT - 100, h = 60, w = 30, head_r = 12} + TEACHER.tx = TEACHER.x + TEACHER.ty = TEACHER.y + +function TEACHER:draw () + -- Hair + love.graphics.setColor(COLOR_HAIR) + love.graphics.circle('fill', self.x, self.y - self.h - self.head_r - 4, self.head_r+1) + love.graphics.rectangle('fill', self.x - 10, self.y - self.h - self.head_r, 10 * 2, self.head_r) + -- Suite + love.graphics.setColor(COLOR_SUIT) + love.graphics.polygon('fill', self.tx, self.ty, self.x - self.w / 2, self.y - self.h, self.x + self.w / 2, self.y - self.h ) + -- Head + love.graphics.setColor(COLOR_HEAD) + love.graphics.circle('fill', self.x, self.y - self.h - self.head_r, self.head_r) +end + +function TEACHER:update (dt) + TEACHER.tx = TEACHER.tx + ( TEACHER.x - TEACHER.tx ) * 6 * dt + TEACHER.ty = TEACHER.ty + ( TEACHER.y - TEACHER.ty ) * 10 * dt +end + +function TEACHER:move (dx, dy) + self.x = math.max(50, math.min(self.x + dx, WINDOW_WIDTH - 50)) + self.y = math.max(250 + 30, math.min(self.y + dy, WINDOW_HEIGHT - 10)) +end + +-------------------------------------------------------------------------------- +-- Text Bobble + +local TEXT_BUBBLE = { tracking = TEACHER, x = 100, y = 100, text = "Hej Markov Chains! ;)", w = 300, h = 100 } + TEXT_BUBBLE.text = 'God morgen alle sammen, velkommen til interaktiones-design forelæsningen.' + +function TEXT_BUBBLE:draw () + -- Draw textbobble + love.graphics.setColor(COLOR_WHITE) + love.graphics.rectangle('fill', self.x, self.y, self.w, self.h, 10) + love.graphics.polygon('fill', self.x, self.y + self.h / 2, self.tracking.x, self.tracking.y - 100, self.x + self.w, self.y + self.h / 2) + + -- Draw text + love.graphics.setFont(TALK_FONT) + love.graphics.setColor(COLOR_BLACK) + local text_height = TALK_FONT:getHeight() * #select(2, TALK_FONT:getWrap(self.text, self.w - 10*2)) + local text_x, text_y = math.floor(self.x + 10 + 0.5), math.floor(self.y + (self.h - text_height) / 2 + 0.5) + love.graphics.printf(self.text, text_x, text_y, self.w - 10*2, 'center') +end + +function TEXT_BUBBLE:update (dt) + local tx, ty = self:getTargetPos() + local cx, cy = self.x + (tx - self.x) * dt, self.y + (ty - self.y) * dt + self.x = math.max(10, math.min(cx, WINDOW_WIDTH - 10 - self.w)) + if ty < cy then cy = self.y + (ty - self.y) * dt * 6 end + self.y = cy +end + +function TEXT_BUBBLE:getTargetPos () + local y_distance = self.h + self.tracking.h + self.tracking.head_r * 2 + 30 + local tx, ty = self.tracking.x - self.w * 0.5, self.tracking.y - y_distance + return tx, ty +end + +local TEXT_BUBBLE_MARKOV_CHAIN + +do + local ALPHABET = {"a","b","c","d","e","f","g","h","i","j","k","l","m", + "n","o","p","q","r","s","t","u","v","w","x","y","z",'æ','ø','å', + '?',',', '.', ' ','0', '1','2','3','4','5','6','7','8','9'} + + local chain = markov.VoMarkovChain.new(ALPHABET, 6) + TEXT_BUBBLE_MARKOV_CHAIN = markov.StringWrapper.new(chain) + + local t = {} + local lines = love.filesystem.lines('/sentences.txt') + TEXT_BUBBLE_MARKOV_CHAIN:train(lines, true) + end + +local function randomize_text_bubble_text () + local text + repeat text = TEXT_BUBBLE_MARKOV_CHAIN:generate(100) + until text:len() > 80 + local first_space_index = text:find(' ') + local final_space_index = text:find' [^ ]-$' + TEXT_BUBBLE.text = ('.' .. text:sub(first_space_index, final_space_index)):gsub("(%. *)(%l)(%w*)", function(c, a,b) return c .. ' ' .. string.upper(a) .. b end):gsub('%s+', ' '):sub(2) +end + +-------------------------------------------------------------------------------- +-- Callbacks + +local TEACHER_TARGET = { x = 0, y = 0 } + +local function randomize_teacher_target () + repeat + TEACHER_TARGET.x = math.random() * (WINDOW_WIDTH - 50*2) + 50 + TEACHER_TARGET.y = math.random() * 100 + 280 + until math.sqrt((TEACHER_TARGET.x-TEACHER.x)^2 + (TEACHER_TARGET.y-TEACHER.y)^2) > 200 +end + +function love.load () + +end + +local TIMER_PROJECTOR, TIMER_TEXT, TIMER_MOVE = 0, 10, 0 +local TIMER_FULL = 15 * 60 + +function love.update (dt) + TEACHER:update(dt) + TEXT_BUBBLE:update(dt) + + -- Update timers + TIMER_PROJECTOR, TIMER_TEXT, TIMER_MOVE = TIMER_PROJECTOR - dt, TIMER_TEXT - dt, TIMER_MOVE - dt + TIMER_FULL = TIMER_FULL + dt + + -- Quit game + if TIMER_FULL > 60 * 60 + 5 then love.event.quit() end + if TIMER_FULL > 60 * 60 then TEXT_BUBBLE.text = 'Hov, den er vist 13:00. Tak for i dag, og vi ses næste uge!'; return end + + -- Events occur + if TIMER_PROJECTOR < 0 then TIMER_PROJECTOR = math.random() * 10 + 5; randomize_projector_color() end + if TIMER_TEXT < 0 then TIMER_TEXT = math.random() * 2 + 6; randomize_text_bubble_text() end + if TIMER_MOVE < 0 then TIMER_MOVE = math.random() * 10 + 5; randomize_teacher_target() end + + -- Move teacher + local dist_x, dist_y = TEACHER_TARGET.x - TEACHER.x, TEACHER_TARGET.y - TEACHER.y + local dist = math.sqrt(dist_x*dist_x + dist_y*dist_y) + local speed = 60 + if dist < 2 then return end + TEACHER:move(speed*dt*dist_x/dist, speed*dt*dist_y/dist) +end + +function love.keypressed (key) + if key == 'f' then + love.event.quit() + end +end + +function love.draw () + draw_background() + draw_clock(WINDOW_WIDTH - 50 + 2, 60, 14, TIMER_FULL) + TEACHER:draw() + TEXT_BUBBLE:draw() + + love.graphics.setFont(OPTIONS_FONT) + love.graphics.print('Muligheder: \n- F: Du lærer intet fra det her, du kan lige så godt bare gå!', 20, WINDOW_HEIGHT - 40) +end diff --git a/2017-uge4/markov.lua b/2017-uge4/markov.lua new file mode 100644 index 0000000..10e732e --- /dev/null +++ b/2017-uge4/markov.lua @@ -0,0 +1,247 @@ + +--[[ + Markov Chain module of the luaFortune library. + + Documentation and License can be found here: + https://bitbucket.org/Jmaa/luafortune +--]] + +local markov = { + version = 0.5 +} + +-------------------------------------------------------------------------------- +-- Constants + +local ENGLISH_ALPHABET = {"a","b","c","d","e","f","g","h","i","j","k","l","m", +"n","o","p","q","r","s","t","u","v","w","x","y","z"} + +-------------------------------------------------------------------------------- +-- Misc functios + +local function reverse_alphabet (alphabet) + local reverse = {} + for index, letter in ipairs(alphabet) do + reverse[letter] = index + end + reverse[""] = 0 + return reverse +end + +-------------------------------------------------------------------------------- + +local function new_chain (alphabet, order) + local alphabet = alphabet or ENGLISH_ALPHABET + return { + alphabet = alphabet, + reverse_alphabet = reverse_alphabet(alphabet), + nr_symbols = #alphabet, + order = order, + } +end + +-------------------------------------------------------------------------------- +-- first-order markov chains + +local MarkovChain = {} + MarkovChain.__index = MarkovChain +markov.MarkovChain = MarkovChain + +function MarkovChain.new (alphabet) + return setmetatable(new_chain(alphabet), MarkovChain) +end + +function MarkovChain:trainMany (list_of_words) + for _, word in ipairs(list_of_words) do + self:trainSingle(word) + end +end + +function MarkovChain:getTotalWeight (loc) + local total_weight = 0 + for i=1, self.nr_symbols do + total_weight = total_weight + (self[loc+i] or 0) + end + return total_weight +end + +function MarkovChain:translateToFinal (id_word) + local word = {} + for i=#id_word, 1, -1 do + word[i] = self.alphabet[id_word[i]] + end + return word +end + +function MarkovChain:translateToRaw (str_word) + local id_word = {} + for i=#str_word, 1, -1 do + id_word[i] = self.reverse_alphabet[str_word[i]] + end + return id_word +end + +function MarkovChain:trainSingle (word) + for i=1, #word do + local cur_id = + self.reverse_alphabet[word[i-1] or ""] * self.nr_symbols + + self.reverse_alphabet[word[i ]] + self[cur_id] = (self[cur_id] or 0) + 1 + end +end + +function MarkovChain:getNextRaw (word, index) + local index = index or #word + local loc = index==0 and 0 or word[index]*self.nr_symbols + local weight_random = math.random(self:getTotalWeight(loc)) + for j=1, self.nr_symbols do + weight_random = weight_random - (self[loc+j] or 0) + if weight_random <= 0 then + return j + end + end +end + +function MarkovChain:getNext (word, index) + local id_word = {} + for i=1, #word do + id_word[i] = self.reverse_alphabet[word[i]] + end + return self.alphabet[self:getNextRaw(id_word, index)] +end + +-------------------------------------------------------------------------------- +-- nth-order markov chains + +local NoMarkovChain = { + trainMany = MarkovChain.trainMany, + getTotalWeight = MarkovChain.getTotalWeight, + translateToFinal = MarkovChain.translateToFinal, + translateToRaw = MarkovChain.translateToRaw +} +NoMarkovChain.__index = NoMarkovChain +markov.NoMarkovChain = NoMarkovChain + +function MarkovChain.new (alphabet, order) + return setmetatable(new_chain(alphabet, order), NoMarkovChain) +end + +function NoMarkovChain:trainSingle (word) + local word_len = #word + local markov_order = self.order + local nr_symbols = self.nr_symbols + for i = markov_order + 1, markov_order + word_len do + local cur_id = 0 + for j=i-markov_order, math.min(word_len,i+markov_order) do + self[cur_id] = (self[cur_id] or 0) + 1 + cur_id = cur_id*nr_symbols+self.reverse_alphabet[word[j]] + end + self[cur_id] = (self[cur_id] or 0) + 1 + end +end + +function NoMarkovChain:getLoc (word, this_i) + local loc = 0 + for i = math.max(1, this_i-self.order+1), this_i do + loc = loc*self.nr_symbols + word[i] + end + return loc +end + +function NoMarkovChain:getNextRaw (word, index) + local index = index or #word + local loc = self:getLoc(word, index) * self.nr_symbols + local total_weight = self:getTotalWeight(loc) + if total_weight == 0 then return nil end + local weight_random = math.random(total_weight) + for j=1, self.nr_symbols do + weight_random = weight_random - (self[loc+j] or 0) + if weight_random <= 0 then + return j + end + end +end + +-------------------------------------------------------------------------------- +-- Variable-order markov chains + +local VoMarkovChain = { + trainSingle = NoMarkovChain.trainSingle, + trainMany = NoMarkovChain.trainMany, + getLoc = NoMarkovChain.getLoc, + getTotalWeight = NoMarkovChain.getTotalWeight, + translateToFinal = NoMarkovChain.translateToFinal, + translateToRaw = NoMarkovChain.translateToRaw, + getNext = NoMarkovChain.getNext, +} +VoMarkovChain.__index = VoMarkovChain +markov.VoMarkovChain = VoMarkovChain + +function VoMarkovChain.new (alphabet, order) + return setmetatable(new_chain(alphabet, order), VoMarkovChain) +end + +function VoMarkovChain:getLoc (word, this_i, min_i) + local loc = 0 + for i = math.max(1, min_i+this_i-self.order+1), this_i do + loc = loc*self.nr_symbols + word[i] + end + return loc +end + +function VoMarkovChain:getNextRaw (word, index) + local index = index or #word + for i=0, index do + local loc = self:getLoc(word, index, i) + local total_weight = self[loc] + if total_weight then + local weight_random = math.random(total_weight) + loc = loc * self.nr_symbols + for j=1, self.nr_symbols do + weight_random = weight_random - (self[loc+j] or 0) + if weight_random <= 0 then + return j + end + end + end + end +end + +-------------------------------------------------------------------------------- +-- String-based markov wrapper + +local StringWrapper = {} + StringWrapper.__index = StringWrapper +markov.StringWrapper = StringWrapper + +function StringWrapper.new (chain) + return setmetatable({chain = chain}, StringWrapper) +end + +function StringWrapper:train (list_of_words, is_iterator) + local char_match = "[\01-\127\192-\255][\128-\191]*" + for _, word in is_iterator and list_of_words or ipairs(list_of_words) do + word = word or _ + local word_table = {} + for char in word:gmatch(char_match) do + table.insert(word_table, char) + end + self.chain:trainSingle(word_table) + end +end + +function StringWrapper:generate (word_len) + local word = {} + for i=1, word_len do + local next_char = self.chain:getNextRaw(word) + word[i] = next_char + if next_char == nil then + break + end + end + return table.concat(self.chain:translateToFinal(word)) +end + +-------------------------------------------------------------------------------- + +return markov diff --git a/2017-uge4/sentences.txt b/2017-uge4/sentences.txt new file mode 100644 index 0000000..6dd58fa --- /dev/null +++ b/2017-uge4/sentences.txt @@ -0,0 +1,68 @@ +vi har en meget tydelig design sprog, og det er selve definitionen af brandet. +du vil aldrig se noget popsmart i et bang og olufsen produkt, hvis vi har gjort vores arbejde ordentligt brugeren vil kunna forstå, hvad historien er. +en af vores regler er to kun skabe produkter med en stærk grund til at være og designet følger. +du vil aldrig fange en af vores designers tegne en skitse af, hvordan et produkt ser ud på papir og derefter forsøger at passe elektronikken ind i det, vi arbejder indefra og ud, so formular virkelig følger funktion. +vores signatur design has fokus på kvalitet, på essentialisme, på at skære tingene tilbage til kernen og enklere alt. +vi also have en stærk forståelse af materiale, ingen form for metal eller træ anvendt because er attraktiv, det hele dræbe et formål. +vores kunder er omdrejningspunktet i vores konceptuelle arbejde. hvert stykke stammer med vores brugere. +det gør vi gennem forskning og tilbringe tid sammen med kunderne to få en forståelse af de smertestillende punkter associeret med teknologi, og hvordan denne tech væver ind i folks liv. +vi finder mønstre i folks adfærd og fra dette, levere meningsfuld løsninger. +vi har ikke designe produkter med kort levetid, og vi forsøger at gøre det muligt for folk, der har købt ind i brand lang tid siden, siger en gammel pladespiller, for at bringe disse produkter i den nye verden med dem. +vi kan kombinere vores klassiske ikoner med de produkter, vi frigiver nu, at sikre de arbejder sammen. +men hvis disse produkter kommer til at vare, de har brug for at blive godt. +vi er en highend brand, og vi går ikke på kompromis med kvaliteten. +fra komponenterne i højttalerne til det materiale, de er indkapslet i, bruger vi de allerbedste materialer. +vi bruger aluminium højttalere, fordi det overfører varme meget godt, og dette virker for god lydkvalitet, og fordi det er holdbart, ikke fordi det er smukt. +en anden måde sørger vi for vores produkter er tidløse i de figurer, vi udfører. +vi starter med en geometrisk design og bruge disse grundlæggende former hele, fordi disse former findes i naturen, øjnene ikke trætte af dem. + +hvor mange interaktiv produkter er der i daglig brug? +tænk et øjeblik over, hvad du bruger i en typisk dag, mobil telefon, computer, fjernbetjening, kaffemaskine, atm, billetautomat, printer, ipod, lommeregner, gps, dvd, computerspil. +listen er endeløs. +nu tænker et øjeblik om, hvordan brugbar de er. +hvor mange er faktisk nemt, ubesværet, og sjovt at bruge? +nogle kan lide ipod er en fornøjelse at bruge. +andre, ligesom data projektor, der ikke ser en persons laptop, når du tilslutter det, kan være meget frustrerende. +hvorfor er der en forskel? +et hovedformål interaktionsdesign er at reducere de negative aspekter af brugeroplevelsen og samtidig øge de positive. +i det væsentlige, det handler om at udvikle interaktiv produkter 1, der er nemme, effektive, og behagelig at bruge, fra brugernes perspektiv. +i dette kapitel starter vi med at undersøge hvad interaktionsdesign er. +vi ser på forskellen mellem god og dårlig design, fremhæver, hvordan produkterne kan afvige radikalt i hvordan brugbare og fornøjelig de er. +vi derefter beskrive hvad og hvem der er involveret i processen med interaktionsdesign. +brugeren erfaring, som er et centralt anliggende for interaktionsdesign, indføres derefter. +endelig har vi skitsere, hvordan at karakterisere brugeroplevelsen i form af usability, brugeroplevelse mål, og design principper. +en opgave præsenteres i slutningen af kapitlet, hvor du har mulighed for at omsætte til praksis, hvad du har læst ved at evaluere udformningen af et interaktivt produkt. +et centralt anliggende for interaktionsdesign er at udvikle interaktive produkter, der er anvendelige. +med dette er generelt menes let at lære, effektivt at bruge, og giver en fornøjelig bruger erfaring. +et godt sted at begynde at tænke over, hvordan man designer brugbare interaktive produkter er at sammenligne eksempler på godt og dårligt designede dem. +gennem identificere specifikke svagheder og styrker forskellige interaktive systemer, kan vi begynde at forstå, hvad det betyder for noget at være brugbart eller ej. +her beskriver vi to eksempler på dårligt designede produkter, en telefonsvarer, system anvendes på hoteller og allestedsnærværende fjernbetjening enhed, og kontrast disse med to veldesignede eksempler på produkter, der udfører den samme funktion. +verden bliver gennemtrængt af teknologier, der understøtter mere forskelligartede aktiviteter. +tænk et øjeblik, hvad du kan i øjeblikket ved hjælp af edb systemer. sende beskeder, indsamle oplysninger, skrive essays, styre kraftværker, program, tegne, planlægge, beregne, overvåge andre, spille spil for at nævne nogle få. +nu tænker de typer af grænseflader og interaktive enheder, der er til rådighed. +også de er lige så forskellige. multirøre skærme, tale baserede systemer, håndholdte enheder, og store interaktive skærme til nævne nogle få. +der er også mange måder at designe, hvordan brugerne kan interagere med et system, f.eks. +ved brug af menuer, kommandoer, formularer, ikoner, gestik osv desuden stadigt mere innovative dagligdags artefakter bliver skabt, ved hjælp af nye materialer, såsom e tekstiler og wearables. +grænsefladerne for dagligvarer, ligesom kameraer, mikrobølgeovne og vask ing maskiner, der plejede at være fysisk og rige produktdesign, er nu predomi overvejende digitalt baseret, kræver interaktion design. +flytningen mod omdanne menneske menneske transaktioner i udelukkende grænseflade baserede dem har også indført en ny type kunde interaktion. +self kasserne på købmandsforretninger, lufthavne, og biblioteker er ved at blive normen, hvor kunderne selv skal kontrollere i deres egen varer, bagage eller bøger. +i stedet for et venligt ansigt hjælpe dem ud, grænseflader bark ordrer på dem. +mens mere omkostningseffektiv, det lægger byrden på brugerne at interagere med systemet. +trykke utilsigtet på den forkerte knap kan resultere i en frustrerende, og undertiden mortifying, oplevelse, især for førstegangsbrugere. +en række udtryk er blevet anvendt til at fremhæve forskellige aspekter af det, der designet, herunder brugergrænseflade design, software design, brugercentreret design, produkt design, webdesign, oplevelsesdesign, og interaktivt system design. +interaktionsdesign bliver i stigende grad accepteret som paraplyen sigt, der dækker alle disse aspekter. +ja, mange praktiserende læger og designere, som i 1990erne ville have beskrevet, hvad de var gør som interface design eller interaktivt system design, nu fremme, hvad de gør som interaktionsdesign. +fokus i interaktionsdesign er meget meget bekymret med praksis, dvs. +hvordan man designer brugeroplevelser. +det er ikke gift med en bestemt måde at gøre design, men er mere eklektisk, fremme brugen af en række metoder, teknikker og rammer. +hvilket er givet prom inence eller er i øjeblikket på mode, vil i høj grad afhænge af den tid og kontekst. +hvorledes interaktionsdesign adskiller sig fra andre tilgange til design af computerbaserede systemer, såsom software engineering? +en simpel analogi til en anden profession, con berørt med skabe bygninger, tydeliggøre denne forskel. +i sin redegørelse for interaktion design, winograd spørger, hvordan arkitekter og ingeniører er forskellige, når de står med problemet med at bygge et hus. +arkitekter er bekymrede med de mennesker og deres interac tioner med hinanden og med huset bliver bygget. +for eksempel er der den rette blanding af familie og private rum? +er rum til madlavning og spisning i umiddelbar nærhed? +vilje mennesker lever i rummet er udformet på den måde, det var beregnet til at blive brugt? +i modsætning, ingeniører interesseret i spørgsmål at gøre med at realisere projektet. +disse omfatter praktisk bekymringer som omkostninger, holdbarhed, strukturelle aspekter, miljømæssige aspekter, brandkrav, og byggemetoder. +ligesom der er en forskel mellem at designe og bygge en hus, så er også der en skelnen mellem at designe et interaktivt produkt og ingeniør ing softwaren til det.