Let's go instruction by instruction.
They are fine. However, with such a few features used from each module, you could explicitly state them so the reader know what to expect:
from random import choice
from time import sleep
A little problem on naming here.
choices is not meaningful in the context as there is nothing to choose from (for a user perspective). Answers or possibilities seems a better fit to me. You might want to use uppercase variable name as it denotes a constant value.
And, since you do not plan on modifying its value during execution, you could enforce that using a tuple instead of a list:
ANSWERS = (
"I don't know",
"I'm too tired",
Also note the spacing around
= and the indentation to improve readability.
Infinite loops are great. They are so great, no one ever stopped using one.
Joking aside, the only way for you users to exit your program is by using ctrl-c. You may want to provide a cleaner way; for instance if they input an empty line:
'Oo' part on the second line of the input feels weird. Try to come up with a more conventionnal prompt delimiter.
Also remember that the text entered by the user will be right after the printed string: add a space for ease of use.
input("Ask the mighty 8-Ball a question\n> ")
range(0, x) is better written as
range(x). It has the exact same meaning but reads better.
By convention, variables of a for loop that are unused in their body are named
for _ in range(3):
You could have a fancier output and use that
i variable if your output uses variable number of dot:
One full second between shakings, meaning 3 seconds before an answer seems pretty long for this kind of program. I would reduce that time a bit to 0.6 or 0.7 seconds:
sleep(.7) # remember the imports
Nothing more to say for the last line. But don't forget to adapt it to the previous changes: