Code Doodles!

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Tst_
Prole
Posts: 7
Joined: Tue Dec 23, 2014 10:11 pm

Re: Code Doodles!

Post by Tst_ »

creeper9207 wrote: Sun Mar 26, 2017 3:18 pm I sadly don't have the code for this, as it was made by mistake when trying to draw a circle.

-snip-
I managed to do something like that once, I was making a radial menu and I found that if you drew polygons as lines instead of filled they do funky stuff.
Image

Never really finished this project but I might do something out of the code I wrote for it some day.
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Code Doodles!

Post by grump »

A simple, quick, and dirty implementation of a depth-search "genetic" algorithm that tries to recreate a reference image using random primitives (noise, lines, circles) and fitness testing.

The reference image I used for testing:
Image

A series of progress images, taken every 250 generations:
Image

3,000 generations took about 15 minutes on my laptop (i7-7700HQ, 2.8 GHz, GTX 1050, using a single core). The last image is made of 158 primitives.

Code: Select all

local rnd = love.math.random

local function randColor()
	return {
		math.floor(rnd(0, 256)),
		math.floor(rnd(0, 256)),
		math.floor(rnd(0, 256)),
		math.floor(rnd(0, 256)),
	}
end

local Line = function(w, h)
	return {
		x1 = rnd(0, w),
		y1 = rnd(0, h),
		x2 = rnd(0, w),
		y2 = rnd(0, h),
		color = randColor(),
		w = rnd(1, 30),

		draw = function(self)
			love.graphics.setColor(self.color)
			love.graphics.setLineWidth(self.w)
			love.graphics.line(self.x1, self.y1, self.x2, self.y2)
		end
	}
end

local Circle = function(w, h)
	return {
		x = rnd(0, w),
		y = rnd(0, h),
		w = rnd(1, 30),
		color = randColor(),
		radius = rnd(0, .5 * math.min(w, h)),
		filled = rnd() < .5,

		draw = function(self)
			love.graphics.setColor(self.color)
			if not self.filled then
				love.graphics.setLineWidth(self.w)
			end
			love.graphics.circle(self.filled and "fill" or "line", self.x, self.y, self.radius)
		end
	}
end

local noise = nil

local Noise = function(w, h)
	if not noise then
		local img = love.image.newImageData(w, h)
		img:mapPixel(function(x, y, r, g, b, a)
			local n = love.math.noise(x + rnd(), y + rnd())
			return 255, 255, 255, 255 * n
		end)
		noise = love.graphics.newImage(img)
	end

	return {
		x = rnd(0, w),
		y = rnd(0, h),
		angle = rnd(0, math.pi * 2),
		sx = rnd(0.1, 10),
		sy = rnd(0.1, 10),
		color = randColor(),

		draw = function(self)
			love.graphics.setColor(self.color)
			love.graphics.draw(noise, self.x, self.y, self.angle, self.sx, self.sy)
		end
	}
end

local shapes = { Noise, Line, Circle, }

local rimg = love.image.newImageData("reference.jpg")
love.window.setMode(rimg:getWidth(), rimg:getHeight() * 3)

local reference = love.graphics.newImage(rimg)
local state = { }
local canvas = love.graphics.newCanvas(reference:getWidth(), reference:getHeight())
local diff = love.graphics.newCanvas(reference:getWidth(), reference:getHeight())
local best = canvas:newImageData()
local bestImage = love.graphics.newImage(best)
local bestFitness = 1e9
local generation = 1
local nochange = 0

local diffShader = love.graphics.newShader([[
	extern Image reference;

	vec4 effect(vec4 color, Image texture, vec2 uv, vec2 fc) {
		vec3 c = abs(Texel(texture, uv).rgb - Texel(reference, uv).rgb);
		return vec4(c.rgb, 1.0);
	}
]])

function fitness()
	local img = diff:newImageData()
	local result = 0
	img:mapPixel(function(x, y, r, g, b, a)
		result = result + r + g + b
		return r, g, b, a
	end)
	return result, img
end

function mutateState()
	local index = rnd(1, #state)
	local rand = rnd()
	if #state == 0 or nochange > 1000 or rand < 1 / #state then
		nochange = 0
		table.insert(state, rnd(1, #state), shapes[rnd(1, #shapes)](reference:getWidth(), reference:getHeight()))
	elseif rand < .25 then
		table.remove(state, index)
	elseif rand < .5 then
		local index2 = rnd(1, #state)
		local temp = state[index]
		state[index] = state[index2]
		state[index2] = temp
	else
		state[index] = shapes[rnd(1, #shapes)](reference:getWidth(), reference:getHeight())
	end
end

function copyState()
	local r = {}
	for i = 1, #state do
		r[i] = state[i]
	end
	return r
end

function love.draw()
	for loop = 1, 256 do
		local old = copyState()
		mutateState()

		canvas:renderTo(function()
			love.graphics.clear(0, 0, 0, 255)
			for i = 1, #state do
				state[i]:draw()
			end
		end)

		love.graphics.setColor(255, 255, 255)

		diff:renderTo(function()
			love.graphics.clear(0, 0, 0, 255)
			love.graphics.setShader(diffShader)
			diffShader:send("reference", reference)
			love.graphics.draw(canvas)
			love.graphics.setShader()
		end)

		local fit, diffImage = fitness()
		if fit < bestFitness then
			best = canvas:newImageData()
			bestImage = love.graphics.newImage(best)
			bestFitness = fit
			generation = generation + 1
			nochange = 0
			if generation % 250 == 0 then
				bestImage:getData():encode("png", string.format("%03d.png", generation / 250))
			end
			break
		else
			nochange = nochange + 1
			state = old
		end
	end

	love.graphics.draw(reference)
	love.graphics.draw(canvas, 0, reference:getHeight())
	love.graphics.draw(bestImage, 0, reference:getHeight() * 2)
	love.graphics.print(bestFitness, 0, 0)
	love.graphics.print("Gen " .. generation .. " (" .. #state .. " primitives)", 0, reference:getHeight() * 2)
end
User avatar
veethree
Inner party member
Posts: 875
Joined: Sat Dec 10, 2011 7:18 pm

Re: Code Doodles!

Post by veethree »

This is somewhat inspired by grump's thing, But significantly less interesting. It "evolves" the complete works of Shakespeare from a random string.

Code: Select all

function love.load()
	goal = "The complete works of Shakespeare."
	current = rndstring(#goal)
	gen = 0
	fit = fitness(current)
	finished = false
	run = false

	local f = love.graphics.newFont(18)
	love.graphics.setFont(f)
	love.graphics.setBackgroundColor(220, 220, 220)

	startTime = 0
	endTime = 0
end

function fitness(str)
	local f = 0
	for i=0, #current do
		local c = str:sub(i)
		local g = goal:sub(i)
		local d = dist(c, g)
		f = f + d
	end
	return f
end

function rndstring(length)
	local s = ""
	local alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 '
	for i=1, length do
		local r = math.random(#alphabet)
		s = s..alphabet:sub(r, r)
	end
	return s
end

function mutatestring(str)
	local r = math.random(#str)
	local s = str:sub(0, r-1)
	local c = str:sub(r, r)
	local e = str:sub(r+1)

	local rd = math.random()

	local new = c
	if rd < 0.5 then
		new = string.char(string.byte(c) - 1)
	else
		new = string.char(string.byte(c) + 1)
	end

	return s..new..e
end

function evolve()
	gen = gen + 1

	local n = mutatestring(current)
	local f = fitness(n)
	if f < fit then
		current = n
		fit = f
	end

	if fit == 0 then
		finished = true
		endTime = love.timer.getTime()
	end
end

function love.update(dt)
	if not finished and run then
		evolve()
	end
end

function love.draw()
	if finished then
		love.graphics.setColor(10, 200, 50)
	else
		love.graphics.setColor(255, 10, 10)
	end
	love.graphics.printf("CURRENT\n"..current.."\n\nGOAL\n"..goal.."\n\nGeneration: "..gen.. " | Fitness: "..fit, 0, 12, 800, "center")

	if finished then
		love.graphics.printf("Finished!\nTime: "..(math.floor( (endTime - startTime) * 1000) / 1000).."\nGenerations: "..gen.."\nPress r to reset", 0, 300, 800, "center")
	end
end

function love.keypressed(key)
	if key == "escape" then love.event.push("quit") end
	if key == "space" then
		run = not run	
		if run then
			startTime = love.timer.getTime()
		end
	elseif key == "r" then
		love.load()
	end
end

function dist(a, b)
	return math.abs(string.byte(a) - string.byte(b))
end
User avatar
jojomickymack
Prole
Posts: 45
Joined: Tue Dec 26, 2017 4:52 pm

Re: Code Doodles!

Post by jojomickymack »

Rectangle mesh tilted back so it's like a chess board and made a waveform of the sketch shader from moonshine. This might be a good game ending animation?
explode.gif
explode.gif (430.94 KiB) Viewed 23206 times
Attachments
sketch002.love
(510.61 KiB) Downloaded 531 times
User avatar
SirRanjid
Prole
Posts: 39
Joined: Sun Nov 19, 2017 1:44 pm
Contact:

Re: Code Doodles!

Post by SirRanjid »

Flow field with some random squre fish things.

You can drag the background with left mouse and apply some movement like scrolling with your smartphone.

Image
flow_field.love
(3.22 KiB) Downloaded 623 times
User avatar
s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Code Doodles!

Post by s-ol »

spiral around a 3d torus very simply projected to 2d and rendered as many circles:
Image
(source)

This eventually led to an this 4d torus explorer program: https://github.com/s-ol/torus4d

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
User avatar
SirRanjid
Prole
Posts: 39
Joined: Sun Nov 19, 2017 1:44 pm
Contact:

Re: Code Doodles!

Post by SirRanjid »

Maybe not a doodle anymore but I've taken the fish to the next level.
Also cleaner and more commented code.

Image
Attachments
particles_flowfield2.love
(6.07 KiB) Downloaded 551 times
User avatar
veethree
Inner party member
Posts: 875
Joined: Sat Dec 10, 2011 7:18 pm

Re: Code Doodles!

Post by veethree »

Why? Eye don't know.
eyedontknow.gif
eyedontknow.gif (769.9 KiB) Viewed 22671 times

Code: Select all

---EYE STUFF
eye = {}
local em = {__index = eye}

local function angle(x1, y1, x2, y2)
	return math.atan2(y2-y1, x2-x1)
end

local function distance(x1, y1, x2, y2)
	return ((x2-x1)^2+(y2-y1)^2)^0.5
end

local function normal(val, min, max)
	return (val - min) / (max - min)
end

function eye.new(x, y, radius, irisRadius, irisColor, pupilDialation)
	local e = {
		x = x,
		y = y,
		dx = x,
		dy = y,
		ix = x,
		iy = y,
		dix = x,
		diy = y,
		radius = radius,
		irisRadius = irisRadius,
		irisColor = irisColor,
		pupilDialation = pupilDialation,
		blinkProgress = 1
	}
	return setmetatable(e, em)
end

function eye:update(dt)
	self.dix = self.dix + (self.ix - self.dix) * 16 * dt
	self.diy = self.diy + (self.iy - self.diy) * 16 * dt

end

function eye:blink()
	self.blinkProgress = 0
end

function eye:draw()
	love.graphics.setColor(1, 1, 1, 1)
	love.graphics.circle("fill", self.x, self.y, self.radius)

	love.graphics.setColor(self.irisColor)
	love.graphics.circle("fill", self.dix, self.diy, self.irisRadius)

	love.graphics.setColor(0, 0, 0, 1)
	love.graphics.circle("fill", self.dix, self.diy, self.irisRadius * self.pupilDialation)

end

function eye:look(x, y)
	local a = angle(self.x, self.y, x, y)
	local d = distance(self.x, self.y, x, y)
	if d > self.radius then d = self.radius end

	self.ix = self.x + (self.radius - self.irisRadius) * (normal(d, 0, self.radius)) * math.cos(a)
	self.iy = self.y + (self.radius - self.irisRadius) * (normal(d, 0, self.radius)) * math.sin(a)
end

--Rest of the shit
function love.load()
	love.window.setMode(800, 600, {msaa = 8} )
	screen = {width = love.graphics.getWidth(), height = love.graphics.getHeight()}
	math.randomseed(os.time() + love.mouse.getX())
	love.graphics.setBackgroundColor(0.2, 0.2, 0.2)
	love.mouse.setVisible(false)
	space = 50
	radius = 32
	color = {math.random(), math.random(), math.random()}
	e = {
		eye.new( (screen.width / 2) - space, (screen.height / 2), radius, radius / 2, color, 0.4 ),
		eye.new( (screen.width / 2) + space, (screen.height / 2), radius, radius / 2, color, 0.4 )
	}
	mx = 0
	my = 0
	h = 0

	blink = 0
	blinkTick = 0
	blinkFrequency = {2, 10}
	nBlink = math.random(blinkFrequency[1], blinkFrequency[2])

	mood = "happy" --happy, concerned, sad

	dist = 0
end

function love.update(dt)
	h = h + 100 * dt
	if h > 255 then h = 0 end
	
	dist = distance(love.mouse.getX(), love.mouse.getY(), screen.width / 2, screen.height / 2)

	if dist > radius * 8 then
		mood = "happy"
	elseif dist > radius * 4 then
		mood = "concerned"
	else
		mood = "sad"
	end

	blinkTick = blinkTick + dt
	if blinkTick > nBlink then
		blinkTick = 0
		nBlink = math.random(blinkFrequency[1], blinkFrequency[2])
		blink = 1
	end

	blink = blink + (0 - blink) * 16 * dt

	mx = mx + (love.mouse.getX() - mx) * 16 * dt
	my = my + (love.mouse.getY() - my) * 16 * dt

	for i,v in ipairs(e) do
		v:update(dt)
		v:look(love.mouse.getX(), love.mouse.getY())
	end

end

function love.draw()
	love.graphics.setColor(232 / 255, 209 / 255, 171 / 255)
	love.graphics.circle("fill", screen.width / 2, screen.height / 2, radius * 4)

	love.graphics.setColor(0, 0, 0)
	if mood == "happy" then
		love.graphics.arc("fill", screen.width / 2, screen.height * 0.58, radius * 1.5, 0, math.pi )
	elseif mood == "concerned" then
		love.graphics.ellipse("fill", screen.width / 2, screen.height * 0.62, radius, radius / 2)
	elseif mood == "sad" then
		love.graphics.arc("fill", screen.width / 2, screen.height * 0.65, radius * 1.5, math.pi, math.pi * 2)
	end

	for i,v in ipairs(e) do
		v:draw()
	end

	--Eyelid
	love.graphics.setColor(232 / 255, 209 / 255, 171 / 255)
	love.graphics.rectangle("fill", (screen.width / 2) - space - radius, screen.height / 2 - radius, (radius * 2) + (space * 2), (radius * 3) * blink)

	love.graphics.setColor(hsl(h, 255, 126))
	love.graphics.circle("fill", mx, my, 8)

end

function love.touchmoved( id, x, y, dx, dy, pressure )
	for i,v in ipairs(e) do
		v:update(dt)
		v:look(x, y)
	end
end

function love.keypressed(key)
	if key == "escape" then love.event.push("quit") end
	if key == "space" then love.load() end
	if key == "f1" then
		love.graphics.captureScreenshot(os.time()..".png")
	end
	if key == "b" then blink = 1 end
end

function hsl(h, s, l, a)
	if s<=0 then return l,l,l,a end
	h, s, l = h/256*6, s/255, l/255
	local c = (1-math.abs(2*l-1))*s
	local x = (1-math.abs(h%2-1))*c
	local m,r,g,b = (l-.5*c), 0,0,0
	if h < 1     then r,g,b = c,x,0
	elseif h < 2 then r,g,b = x,c,0
	elseif h < 3 then r,g,b = 0,c,x
	elseif h < 4 then r,g,b = 0,x,c
	elseif h < 5 then r,g,b = x,0,c
	else              r,g,b = c,0,x
	end return (r+m),(g+m),(b+m),a
end
Madeline Ladd
Prole
Posts: 3
Joined: Tue Aug 22, 2017 6:07 pm

Re: Code Doodles!

Post by Madeline Ladd »

Woah these are so cool! :D
User avatar
SirRanjid
Prole
Posts: 39
Joined: Sun Nov 19, 2017 1:44 pm
Contact:

Re: Code Doodles!

Post by SirRanjid »

First actual doodle. It's a bit psychedelic and plays seemlessly forever.

(No worries no fullscreen and console in case you can't close it directly for some reason like me. Maybe you could tell me what's wrong.)
DON'T disable the event module at least not in v0.10.2 .

Image
Attachments
tri.love
(2.48 KiB) Downloaded 524 times
Post Reply

Who is online

Users browsing this forum: No registered users and 57 guests