GDScript Code Golf

Code golf is a way of writing code to make it as short as possible. It’s completely impractical for making a game, but I think it’s a fun exercise for me to try at least.

So, let’s say we have a basic 2D platformer character controller script. I’d normally write it like this:

extends CharacterBody2D

@export var speed := 600
@export var gravity := 1_000
@export var jump_velocity = -700


func _physics_process(delta: float) -> void:
	velocity.y += gravity * delta
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = jump_velocity
	var h_input := Input.get_axis("left", "right")
	velocity.x = h_input * speed
	move_and_slide()

However, if we’re playing code golf, there’s a lot of characters that can be cut out without the player noticing. Let’s start by removing all the @export variables and replacing them with magic numbers:

extends CharacterBody2D

func _physics_process(delta: float) -> void:
	velocity.y += 1000 * delta
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = -700
	var h_input := Input.get_axis("left", "right")
	velocity.x = h_input * 600
	move_and_slide()

This may make the code harder to adjust, but it’s shorter now! Next, let’s remove all the type hinting; we don’t need it. And let’s rename all the variables and actions to something shorter.

extends CharacterBody2D

func _physics_process(d):
	velocity.y += 1000 * d
	if Input.is_action_just_pressed("j") and is_on_floor():
		velocity.y = -700
	var h = Input.get_axis("l", "r")
	velocity.x = h * 600
	move_and_slide()

We have so much wasted space on… actual space characters. Let’s get rid of them!

extends CharacterBody2D

func _physics_process(d):
	velocity.y+=1000*d
	if Input.is_action_just_pressed("j")&&is_on_floor():
		velocity.y=-700
	var h=Input.get_axis("l","r")
	velocity.x=h*600
	move_and_slide()

Next, let’s get rid of the creation of any new variables by putting all the velocity calculations on a single line with ternary if.

extends CharacterBody2D

func _physics_process(d):
	velocity=Vector2(Input.get_axis("l","r")*600,-700 if Input.is_action_just_pressed("j")&&is_on_floor()else velocity.y+1000*d)
	move_and_slide()

Man, the class of the node takes up so much space! Let’s avoid this by moving the script to a parent node of the CharacterBody2D that extends Node instead to save space! While we’re at it, let’s switch from _physics_process to _process since the player won’t notice. The player is named p to make the get_node as short as possible as well.

extends Node

func _process(d):
	$p.velocity=Vector2(Input.get_axis("l","r")*600,-700 if Input.is_action_just_pressed("j")&&$p.is_on_floor()else$p.velocity.y+1000*d)
	move_and_slide()

Finally, we can remove all the newline characters by replacing them with semicolons.

extends Node;func _process(d):$p.velocity=Vector2(Input.get_axis("l","r")*600,-700 if Input.is_action_just_pressed("j")&&$p.is_on_floor()else$p.velocity.y+1000*d);$p.move_and_slide()

The original code had 389 characters, while the final code only has 184! That’s a difference of 205 characters and a reduction of 53%!

Let me know if there are even more ways to reduce the character count! Input.is_action_just_pressed() is such a long function call :sob:

3 Likes

And here I thought code golf died along with Perl.

3 Likes

Input.is_action_pressed()

My productivity is surging… overflowing!

1 Like

179

extends CharacterBody2D;func _process(d):velocity=Vector2(Input.get_axis("l","r")*600,velocity.y+1000*d-int(Input.is_action_just_pressed("j")&&is_on_floor())*700);move_and_slide()
3 Likes

173

extends CharacterBody2D;func _process(d):velocity.x=Input.get_axis("l","r")*600;velocity.y+=d*1000-int(Input.is_action_just_pressed("j")&&is_on_floor())*700;move_and_slide()
3 Likes

Huh, so you can save characters by having more lines :thinking:

Having more lines lets you save in other places.

Perl or C are much better for golfing though. Not to mention Lisp. GDScript/Python discourage it by design.

3 Likes

Yeah gdscript has a bunch of long function names :sob:

Here’s a fully functional snake in less than 1000 characters, 932 to be precise. Just paste onto a Node2D and run:

extends Node2D
var s=[];var t={};var m=Vector2i.UP;var l;var a;
func _draw():for r in t:draw_rect(Rect2(r*32,Vector2.ONE*31),t[r])
func _ready():
	for i in 17:o(0,i);o(16,i);o(i,0);o(i,16)
	Engine.physics_ticks_per_second=6;x(Vector2i(8,8));b();l=Label.new();l.text="0";add_child(l);l.position=Vector2(550,0)
func o(x,y,c="#a0a0a0"):t[Vector2i(x,y)]=c
func x(p):var b=t.get(p);s.push_front(p);o(p.x,p.y);return b=="#ff8800"
func b():while true:a=Vector2i(randi_range(1,15),randi_range(1,15));if !t.has(a):o(a.x,a.y,"#ff8800");break
func _input(e):if e is InputEventKey&&e.is_pressed():m=Vector2i(Input.get_vector("ui_left","ui_right","ui_up","ui_down"));\
if!is_physics_processing():get_tree().reload_current_scene()
func _physics_process(d):
	if x(s[0]+m):l.text=str(l.text.to_int()+1);b()
	else:t.erase(s[-1]);s.pop_back()
	if!Rect2i(1,1,15,15).has_point(s[0])||s.slice(1).has(s[0]):set_physics_process(false);l.text+="\nGAMEOVR"
	queue_redraw()
5 Likes

That is a cool golfed snake! Someone needed a distraction!

I gave up with code golf when people kept winning with esoteric languages built for golf and could solve virtually anything in like 6 chars or something genuinely annoyingly short.

It never ceases to amaze me, that even today, with your golfed godot quick and dirty version, snake still remains fun to play! It really is a classic!

Are warnings allowed in Godot Golf?

PS You could save some chars with an even shorter version of GAMEOVR. (Since the E is already missing). I would suggest ‘END’ or ‘X’

3 Likes

I could have sworn that I typed “GMEOVR”.

“END” is short but not hackery enough. It should probably be “6M30VR”. I’m willing to sacrifice a couple of characters for more street cred.

Warnings should be fine. If it runs - it qualifies. And yeah, GDScript is particularly hostile to pulling off clever golf moves. Which makes it interesting.

4 Likes

Yeah so I’m sticking with gdscript for code golf so I don’t have to worry about languages designed for code golf. Plus I know gdscript better than those :joy:

that is pretty cool! but it seems a bit buggy for me, since the snake breaks if I press two arrow keys at once

1 Like

Don’t press two keys. I may fix it. There’s still 70 characters left to 1000.

There, fixed. Overly more graceful control handling. It also ignores if you try to go backwards. Still under 1K characters.

extends Node2D
var s=[];var t={};var m=Vector2i.UP;var l;var a;var c="#ff8800";var n
var q=4194319;var k={q+1:m,q+3:Vector2i.DOWN,q:Vector2i.LEFT,q+2:Vector2i(1,0)}
func _draw():for r in t:draw_rect(Rect2(r*32,Vector2.ONE*31),t[r])
func _ready():
	Engine.max_fps=6;x(Vector2i(8,8));l=Label.new();l.text="-1";add_child(l);l.position.x+=550;for i in 17:o(0,i);o(16,i);o(i,0);o(i,16)
func o(x,y,c="#a0a0a0"):t[Vector2i(x,y)]=c
func x(p):n=t.get(p);s.push_front(p);o(p.x,p.y);return n==c
func _input(e):
	if e is InputEventKey&&e.is_pressed():n=k.get(e.keycode,m);m=n if n!=-m else m;if!is_processing():get_tree().reload_current_scene()
func _process(d):
	if t.has(s[0]+m)&&s[0]+m!=a:set_process(0);l.text+="-GMOVR"
	elif x(s[0]+m)||!a:
		l.text=str(l.text.to_int()+1);while 1:a=Vector2i(randi()%15+1,randi()%15+1);if!t.has(a):o(a.x,a.y,c);break
	else:t.erase(s[-1]);s.pop_back()
	queue_redraw()
5 Likes

Nice! You can save more characters by replacing true and false with 1 and 0, since I guess Godot automatically casts them to bool.

Yeah, it could be squeezed some more at places. My primary goal was drop below 1K chars. After that I didn’t really bother to go further.

1 Like

I was thinking, what if you saved characters by having a Timer node with its timeout signal connected to the queue_redraw() function? That way you don’t have to put it in the script.

But I’m not sure if shortening the code by using the GUI to do stuff falls under code golf.

Well then it wouldn’t just run when pasted. Same for assuming the short named actions are defined. Otherwise it would require creating nodes from code which is too much code.
I squeezed it some more. Updated the last posted code. It’s now just under 900 chars.

1 Like

That makes sense if you are working under those constraints. I guess it’s a separate challenge to make a codeless game

1 Like