There is a top-level (or bottom-level, as you might prefer to view it) language called op-codes which the computer’s CPU can read and understand at the hardware level. This language does have commands, variables, and conditions. While the CPU is of course electronic (effectively just a really fancy and miniaturized circuit board), you can view it as nothing more than a bunch of gears and levers. You set some levers – like to choose some input numbers and an operation like “add” – turn a crank, and it spits out a value.
Unlike a machine where you need human operators to manually choose and pull levers, the CPU can pull in instructions, set its own levers, and then read in more instructions for where to stick the output values. You can view it as being similar to taking a long sheet of metal and cutting long slots into it that each correspond to a particular lever. As you push the metal sheet sideways, it raises and lowers the levers automatically with the rise and fall of the slots: That’s op-codes.
One can program directly for the CPU, writing the human legible version of op-codes. This is called Assembly Language. It’s not actually “a” language since different types of CPU have entirely different mechanics. You couldn’t take the metal sheet for one machine and apply it to the levers of another, unless the two machines were configured to have the same interface. As it happens, nearly all CPUs in modern machines are made to be compatible with the Intel 80386 CPU, so you can write Assembler for that CPU and run it on most any desktop.
But if you know that all computers can at a certain level add, subtract, multiply, and send data to or receive binary data from some reasonably standardized interfaces (like a command prompt, keyboard, mouse, monitor, etc.) then you can invent a language which has a generic way of representing math and interacting with peripheral devices. If people then code in that language, you can run what they wrote through a second program (a compiler) which knows how to translate what they wrote into CPU-specific op-codes. For conversation’s sake, we’ll call this language Z.
Of course, once you’ve got a standardized interface for dealing with the monitor – like to choose a pixel and set it’s color – you might build up a bunch of code that draws windows, toolbars, a mouse cursor, etc. That’s not very useful in and of itself. Making a program which has some windows and toolbars in it is pointless, unless those elements actually do something (like word processing or browsing the internet). So you don’t want to make a full application. This you want a compiler to also allow you to build out a “library”.
A library is a package of code that’s been converted to op-codes by a compiler, but that can’t be run like a program. It’s just new commands that people can call in their application, as if the Z language now has more commands than it used to.
And then eventually people determine that there’s no particular point in compiling an application at all. Instead of making your compiler translate Z into op-codes and save it, so you have to go and run your program separately, you can just tell the “compiler” to read in the source code, and as it translates commands, to reroute those op-codes to the CPU right then. Now it’s not a compiler, it’s an “interpreter”.
You also have the option of writing a Z compiler in Z itself. You just have to write the first compiler in assembler.
But so long story short: The CPU understands SOME commands and conditions. Computer programming languages actually hide some of the commands that the CPU can handle, but then they may add more via libraries or by implicitly knowing how to expand your one line of code into hundreds of lines of assembler (effectively, the “library” is built right into the compiler). As you program, when you create objects and methods, you’ll be building up new commands that you’re adding to the bunch. For instance, right now you have been given commands like “turn left” and “walk forward”. But you could create a new command called “wander like a drunken bastard” where the robot uses the “turn left”, “turn right”, and “walk forward” commands randomly for a little bit. Then you can call your new command “wander like a drunken bastard” and because you’ve stringed together the old commands randomly inside of your new command, you get the achieved effect. If you wanted to be able to do the drunken walk, you can just call your one method and it will, internally, call the others. That way you don’t have to rewrite the same code over again, and you can package your command up in a library and (potentially) sell it to others to use.