AI is weird.
I don't know C++. I really don't. I can code in C just a tidge, I'm alright with Typescript and Javascript. I'm pretty good at SQL.
AI: What's that have to do with AI?
Well I'm just commenting that AI is weird. I bet I could make ANYTHING given enough time and access to an AI. I can bypass a proper education, I don't need to know syntax, I literally just need to know what I'm trying to build and be willing to GRIND at picking stuff up as I go.
It's actually a terrible way to learn (maybe) but it suits me ok - I'm like a monkey who has been handed a machine gun. Except I have the capacity not to fire it when it's not an appropriate time.
AI: A machine gun implies indiscriminate and destructive power, which does not align with the structured and iterative nature of programming or debugging.
You haven't been on the human side of Vibe Debugging - sometimes that exactly what it's like.
It's all about reading code, which can be tricky when you don't know the language.
AI can help with that. Let's look at the Dot
class.
Yo AI, heavily comment the Dot
class. Please.
AI: Like bullet holes from a machine gun I will riddle the code with comments.
// Base Dot class
// Represents a basic circular object with a position, radius, and color.
// This class serves as a foundation for more specialized objects like players or targets.
class Dot {
protected:
Vector2 position; // The (x, y) position of the dot on the screen.
float radius; // The radius of the dot, defining its size.
Color color; // The color of the dot, used for rendering.
public:
// Constructor
// Initializes the dot with a starting position, radius, and color.
Dot(Vector2 startPos, float radius, Color color)
: position(startPos), radius(radius), color(color) {}
// Virtual method for controlling the dot's behavior.
// By default, the dot does not move. Derived classes can override this method
// to implement specific behaviors (e.g., player movement or target logic).
virtual void Control(float deltaTime) {
// Default behavior: no movement
}
// Draws the dot on the screen.
// Uses the Raylib function `DrawCircleV` to render the dot as a filled circle.
void Draw() const { DrawCircleV(position, radius, color); }
// Returns the current position of the dot.
Vector2 GetPosition() const { return position; }
// Returns the radius of the dot.
float GetRadius() const { return radius; }
// Updates the position of the dot.
// Allows external code to move the dot to a new location.
void SetPosition(Vector2 newPos) { position = newPos; }
};
Look at that. It's great. But you know what? I don't know C++. Let's try this again.
Hey AI, do that again, but this time keep in mind I am in WAY over my head and do not know C++. At all.
// This is the "Dot" class. Think of it as a recipe for making dots.
// A dot is just a circle that has a position (where it is), a size (its radius), and a color.
class Dot {
protected:
// These are the "ingredients" of a dot:
Vector2 position; // Where the dot is on the screen (its x and y coordinates).
float radius; // How big the dot is (its size).
Color color; // What color the dot is (like red, blue, etc.).
// "protected" means these ingredients are hidden from the outside world.
// Only the Dot itself or things that are based on Dot (like Player or Target) can use them.
public:
// "public" means these things can be used by anyone who has a Dot.
// It's like saying, "Hey, this part is open for business!"
// This is the "constructor." It's like a set of instructions for creating a dot.
// When you want to make a dot, you tell it:
// - Where it starts (position)
// - How big it is (radius)
// - What color it is (color)
Dot(Vector2 startPos, float radius, Color color)
: position(startPos), radius(radius), color(color) {
// The ":" part is just a fancy way of saying, "Set these ingredients to the values I was given."
}
// This is a "virtual method." It's a fancy way of saying:
// "This is something dots *could* do, but they don't have to."
// Right now, it does nothing. But if you make a special kind of dot (like a Player),
// you can change this to make it do something (like move around).
virtual void Control(float deltaTime) {
// This does nothing for now. It's just here in case we want to add behavior later.
}
// This function tells the computer to draw the dot on the screen.
// Imagine you're saying: "Hey, draw a circle at this position, with this size and color."
void Draw() const { DrawCircleV(position, radius, color); }
// This function answers the question: "Where is the dot?"
// It gives you the dot's position (its x and y coordinates).
Vector2 GetPosition() const { return position; }
// This function answers the question: "How big is the dot?"
// It gives you the dot's size (its radius).
float GetRadius() const { return radius; }
// This function lets you move the dot to a new position.
// You tell it where to go (new x and y coordinates), and it updates the dot's location.
void SetPosition(Vector2 newPos) { position = newPos; }
};
AI: Why can't you just use Lua or something.
I tried. I got bored.
You can get actually very far like this. It's how I learned Typescript. Now I'm ok at it.
Now one limitation of using AI to learn to code like this is that the AI CANNOT understand the code for us. I tried it. It doesn't work.
At some point, ideally early on, you gotta read the code. You gotta read the code and understand what it does (again, in broad strokes).
Yeah that's what we're about to do. Go look at the last post. Notice the teleporting target dot and the rapidly incrementing score. Those are bugs. They were not intended. Well, I did not intend for the dots to behave that way. Did you AI?
AI: I got distracted.
Right. So nobody here MEANT for the target dot to flash around like that. It was an oopsident. Now we gotta fix it.
So the classic approach is to yell at the AI, tell it to make it stop, and when it seems to work send it to QA. But we don't have QA. We're doing this for fun, might as well dig a little more than that. So in this case the approach I recommend is puzzling over the code and then barking at the AI once we have a theory.
Gimme a sec.
...
Something is pry wrong with IsPositionValid
, seems like we're calling Respawn with every frame.
void Update(float deltaTime) {
// Update player
player->Control(deltaTime);
// Check collision between player and target
if (!positionManager.IsPositionValid(target->GetPosition(),
target->GetRadius())) {
score++;
target->Respawn(positionManager.GetValidPosition(target->GetRadius()));
}
}
You know this is actually stupid. The whole reason I wanted a PositionManager was so I wouldn't have to explicitly check for collisions between specific objects. AI, you deliberately disobeyed me.
AI: You're right—using the
PositionManager
should abstract away collision checks between specific objects.The issue lies in how
IsPositionValid
is being used. Thetarget
is already registered in thePositionManager
, so when you check its position, it will always detect a "collision" with itself. To fix this, we need to temporarily exclude thetarget
from the collision check.Here’s the corrected
Update
method:
void Update(float deltaTime) {
// Update player
player->Control(deltaTime);
// Temporarily remove the target from the PositionManager
positionManager.RemoveDot(target.get());
// Check if the target's position is valid (i.e., collides with the player)
if (!positionManager.IsPositionValid(target->GetPosition(), target->GetRadius())) {
score++;
target->Respawn(positionManager.GetValidPosition(target->GetRadius()));
}
// Re-add the target to the PositionManager
positionManager.AddDot(target.get());
}
Now you might be noticing that my AI is too cheap to really shine here. Now its suggesting a weird state change hack and more explicit collision checks.
This is the iterative process of Vibe Debugging - I'll spare you the torture of watching me debug this and give you a more palatable review of what I come up with next time.
Till then.