# Make an ASCII bat fly around an ASCII moon

Background

Here is a Halloween related challenge.

As you may have seen from my last challenge I quite like what I term as ascii art animations, that is not just draw a pattern but draw a pattern that progresses. This idea came to me after I was asked a couple of years ago to liven up a (rather dull) presentation by making random ascii bats fly across the screen on Halloween. Needless to say I duly obliged (I was being paid for it) but it made me think that there is more to life than random bats. Inspired by this I would like to propose this challenge.

Challenge

Make a bat fly around the moon.

Here is a bat:

^o^


Here is the moon:

     mmm
mmmmmmm
mmmmmmmmm
mmmmmmm
mmm


You must show every stage of the bats flight (see output).

Input

None

Output

      ^o^
mmm
mmmmmmm
mmmmmmmmm
mmmmmmm
mmm

mmm^o^
mmmmmmm
mmmmmmmmm
mmmmmmm
mmm

mmm
mmmmmmm^o^
mmmmmmmmm
mmmmmmm
mmm

mmm
mmmmmmm
mmmmmmmmm^o^
mmmmmmm
mmm

mmm
mmmmmmm
mmmmmmmmm
mmmmmmm^o^
mmm

mmm
mmmmmmm
mmmmmmmmm
mmmmmmm
mmm^o^

mmm
mmmmmmm
mmmmmmmmm
mmmmmmm
mmm
^o^

mmm
mmmmmmm
mmmmmmmmm
mmmmmmm
^o^mmm

mmm
mmmmmmm
mmmmmmmmm
^o^mmmmmmm
mmm

mmm
mmmmmmm
^o^mmmmmmmmm
mmmmmmm
mmm

mmm
^o^mmmmmmm
mmmmmmmmm
mmmmmmm
mmm

^o^mmm
mmmmmmm
mmmmmmmmm
mmmmmmm
mmm

^o^
mmm
mmmmmmm
mmmmmmmmm
mmmmmmm
mmm


Rules

• No reading from external files or websites
• You may submit a full program or a function
• Extra spaces and/or newlines are fine by me
• Standard loopholes banned as usual
• The bat must finish back at the top of the moon
• Please feel free to clear the screen between frames if you wish however this is not a requirement. Output as shown above is fine
• As this is code golf, the winner will be the answer with the lowest byte count although any answer is welcome

Sample

Reference implementation in totally ungolfed Python 2 (620 bytes but just to prove that it can be done. May golf it later).

b='^o^'
m=['                     ',
'         mmm         ',
'       mmmmmmm       ',
'      mmmmmmmmm      ',
'       mmmmmmm       ',
'         mmm         ',
'                     ']
p=[(9,12),(12,15),(14,17),(15,18),(14,17),(12,15),(9,12),(6,9),(4,7),(3,6),(4,7),(6,9),(9,12)]
d=0
for z in range(7):
x=map(str,m[z])
c="".join(x[:p[z][0]]) + b + "".join(x[p[z][1]:])+"\n"
print "\n".join(m[:z]) + "\n" + c+"\n".join(m[z+1:])
for z in range(6)[::-1]:
x=map(str,m[z])
c="".join(x[:p[z+6][0]]) + b + "".join(x[p[z+6][1]:])+"\n"
print "\n".join(m[:z]) + "\n" + c+"\n".join(m[z+1:])


Result

Although @Jonathan obviously wins on byte count with Jelly, I am going to mark the Brainfuck answer from @Oyarsa as the accepted answer purely because I think that anyone who can actually do something like this in such a crazy language deserves +15 rep no matter how many bytes it takes. This is not because I have any problem with golfing languages. See my answer to a question regarding this on meta if you have any doubts. Many thanks and respect to all who contributed in whatever language.

• Thanks @Oliver. I forgot to remove it after the Sandbox. Oct 17, 2016 at 16:19
• @daHugLenny Thanks for adding the tag. I hadn't thought of that one. Oct 17, 2016 at 18:46
• If you think that reference implementation is totally ungolfed, I never want to work with you on any programming project.
– Nic
Oct 19, 2016 at 13:36
• Well, maybe a bit golfed then :-) Oct 19, 2016 at 13:42

# 05AB1E, 6962 60 bytes

Saved 2 bytes thanks to Adnan.

3ð×…^o^)U13FNV0379730vð5y>;ï-y_+×N(Y-12%_Xè'my×NY-12%_y&XèJ,


Try it online!

Explanation

3ð×…^o^)U stores the list [" ","^o^"] in X for later use.

13FNV loops over the 13 stages [0 .. 12] and stores the current iteration index in Y.

0379730v loops over the rows of each stage,
where N is the row index and y is the current number of m's.

We start by adding floor(5/(y+1))-(y==0) spaces to each row with ð5y>;ï-y_+×.

We then determine if there should be a bat or 3 spaces before the m's.
If (-N-Y)%12 == 0 is true we add a bat, else 3 spaces.
This expression (N(Y-12%_Xè) will place bats in stages 0,6-12.

Then we place y m's with 'my×.

Now we determine if there should be a bat or 3 spaces after the m's.
The code NY-12%_y&Xè will place a bat if ((N-Y)%12 == 0) and y!=0 is true, else 3 spaces.
This will place the bats on stages 1-5.

Finally we join the whole row into a string and print with a newline: J,.

• Does exactly what it says on the tin +1 Oct 17, 2016 at 18:16
• 3ð×…^o^) instead of " ^o^"2ä should save two bytes. Oct 18, 2016 at 16:50
• @Adnan: Thanks! For some reason I just assumed it would be longer and never even tried 3ð×. Oct 18, 2016 at 17:13

## JavaScript (ES6), 109144140 138 bytes

f=(k=13,b=y=>(y-k)%12?'   ':'^o^')=>k--?[0,3,7,9,7,3,0].map((n,y)=>' '.repeat(5-n/2+!n)+b(y)+'m'.repeat(n)+b(n?-y:.1)).join
+
+f(k):''

console.log(f());

### Animated version

f=(k,b=y=>(y-k)%12?'   ':'^o^')=>[0,3,7,9,7,3,0].map((n,y)=>' '.repeat(5-n/2+!n)+b(-y)+'m'.repeat(n)+b(n?y:.1)).join

var k =0;
setInterval(function() { o.innerHTML = f(k++) }, 150);
<pre id=o></pre>

• nice answer and you are the first as on my last challenge :) Oct 17, 2016 at 17:34
• Nice answer. I can't wrap my head around thinking like this. Oct 18, 2016 at 21:50

## HTML+JS, 153 149 bytes

n=setInterval(_=>o.innerText=zzd
zc3e
b7f
a9g
l7h
zk3i
zzj.replace(/\S/g,c=>parseInt(c,36)-n%12-10?m.repeat(c)||   :^o^,n++),1e3)
<pre id=o>

Edit: Saved a bunch of bytes thanks to @RickHitchcock. The boring version that just returns the 13 multiline strings in an array is 132 131 bytes:

_=>[...Array(13)].map((_,n)=>zza
zl3b
k7c
j9d
i7e
zh3f
zzg.replace(/\S/g,c=>parseInt(c,36)-n%12-10?m.repeat(c)||   :^o^))

• If I was being really strict I would point out that the bat doesn't start at the top :) But then it does the full circle and depending on opinion finishes at the top which was all that I specified in the rules. +1 for the first full animation. Nice job and thanks. Oct 17, 2016 at 21:14
• @ElPedro I think the place where the bat starts depends on the result of setInterval, which is always the same on my browser, but may vary for other browsers.
– Neil
Oct 17, 2016 at 21:18
• OK, but as I said the start position was not clearly specified so I'm ok to let setInterval decide ;) I'm viewing with Chome on Ubuntu if thet's of any interest. Always starts one position before the top but that doesn't matter. Great solution and thanks again for your answer.. Oct 17, 2016 at 21:33
• You can save a couple bytes by using innerHTML instead of textContent. And another byte by changing repeat(+c) to repeat(c). Oct 18, 2016 at 18:12
• (And 1000 can become 1e3.) Oct 18, 2016 at 18:19

# Jelly, 76 69 58 bytes

⁾ mṁ11ż“æ¤¿¬®µ‘ŒḄ¤Œṙs⁴Y
_4¢ḣ;“^o^”;¢ṫ⁸¤
“½œ0CSbp\I7'ð½‘Ç€Y


TryItOnline

### How?

⁾ mṁ11ż“æ¤¿¬®µ‘ŒḄ¤Œṙs⁴Y - Link 1, make a moon (niladic)
⁾ m                     - literal " m"
ṁ                    - reshape like
11                  - 11 (interpreted as range(11)) -> " m m m m m "
“æ¤¿¬®µ‘         - code page indexes [22,3,11,7,8,9]
ŒḄ       - bounce -> [22,3,11,7,8,9,8,7,11,3,22]
ż                 - zip -> [[22,' '],[3,'m'],[11,' '],...]
Œṙ    - run length decode -> "                      mmm           mmmmmmm        mmmmmmmmm        mmmmmmm           mmm                      "
s   - split into chunks of length
⁴  - l6
Y - join with line feeds

_4¢ḣ;“^o^”;¢ṫ⁸¤ - Link 2, a moon with a bat placed: endIndex
_4              - subtract 4 (startIndex)
;           - concatenate
“^o^”      - literal "^o^"
;     - concatenate
ṫ⁸  - tail from endIndex

“½œ0CSbp\I7'ð½‘    - code page indexes [10,30,48,67,83,98,112,92,73,55,39,24,10]
-    indices to the right of each bat in a constructed moon
Y - join on line feeds

• Very nice. Think I'm going to have to look into Jelly a bit closer. Oct 17, 2016 at 19:53
• Great explanation. Thanks. Oct 17, 2016 at 20:17
• Would be cool to see how you approach this in Python as well if you're interested and have the time. I'm always happy to learn. Oct 17, 2016 at 21:43

# Python 2, 146 144 138 bytes

-2 bytes thanks to @Willem (use the variable c rather than looping through a map)

m='\n'.join(int(a)*' '+int(b)*'m'+3*' 'for a,b in zip('6643466','0379730'))
for b in" -;L[hrbQ@2' ":c=ord(b)-23;print(m[:c-3]+'^o^'+m[c:])


repl.it

'6643466' is the number of spaces before the moon (with 6 on the empty first and last lines, since the bat will go there).

'0379730' is the number of 'm's in the moon on each line.

The zip unpacks these characters into a and b and makes the moon in the sky, m, with 3 trailing spaces on each line.

The last line then traverses the positions of the bat within the moon, and is effectively:

for c in(9,22,36,53,68,81,91,75,58,41,27,16,9):print(m[:c-3]+'^o^'+m[c:])


but the tuple of positions is encoded as the ordinals of printable characters, with an added 23 (since 9 is not printable). This comes out as " -;L[hrbQ@2' ", and c=ord(b)-23 is used to extract the values.

• Respect +1 and thanks for rising to the challenge :) Now I just have to work out how it works... Oct 17, 2016 at 22:49
• Sometimes I wish I could give 2 upvotes. Oct 17, 2016 at 22:55
• You can save two chars on the last line by writing it like this: for b in" -;L[hrbQ@2' ":c=ord(b)-23;print(m[:c-3]+'^o^'+m[c:]) Oct 18, 2016 at 13:28

# Brainfuck, 690 Bytes

This is my first time golfing, so I'm sure there's still plenty of room for improvement

-[-[-<]>>+<]>-<<+++[>+++++++<-]>>>+>>-[<-->-------]<->++++++++>+>+++++>+>+++++>+>+++++++>+>+++++>+>+++++>+>++++++++>+>+>+>>+++[<++++++>-]<+>+>-->+>+>+[<]>[[<+>>-[>]+[[<]>+[>]<+[<]>>>-]<<[->>+<<]>-]>-[->>>>+<<<<]>++]>>>[[-]<]>>>>[>]>>+>+++++++<<<<[<]+<+<<+[[<]++++[>++++<-]>[<+[>]>[-<+>]<[<]>>-]>[>]++++++++++[>]>>>-[<]<<[<]<]<[<]>->-<<<[-]++++++<+<+++[>++++++<-]+<++++[>++++++++<-]---<-[>+<-----]++<----[>+<----]--<-[>+<---]<-[>++<-----]++++<-[>+<---]++<-[>+<-------]---<----[>+<----]<--[>+<++++++]<+++++[>+++++<-]++++++>[[[>]>[>]>>[-<<+>>]<<[<]<[<]>-]>[>]>[>]+++++++[>+++++++++<-]>-[->+>+>+<<<]++++[>>++++<<-]>>+<<<<[<]>[.>]>>[.>]<[<]>[-]>[-]>[-]<+[--<---[->]<]<[->+>+>+<<<]<<[[->>+<<]<]<]


Try it online

-[-[-<]>>+<]>-<<+++[>+++++++<-]>>>+>>-[<-->-------]<->++++++++>+>+++++>+>+++++>+>+++++++>+>+++++>+>+++++>+>++++++++>+>+>+>>+++[<++++++>-]<+>+>-->+>+>+
Sets counters for creation of the m's and spaces
[<]>[[<+>>-[>]+[[<]>+[>]<+[<]>>>-]<<[->>+<<]>-]>-[->>>>+<<<<]>++]
Adds the first counter plus two of the character at the second counter to the end of the
current set of cells removes the first two counters on the list then repeats until
it encounters a 254
>>>[[-]<]>>>>[>]>>+>+++++++<<<<[<]
Removes some excess cells then adds a 7 after the end of the set of cells
+<+<<+[[<]++++[>++++<-]>[<+[>]>[-<+>]<[<]>>-]>[>]++++++++++[>]>>>-[<]<<[<]<]
<[<]>->-<<<[-]++++++<+<+++[>++++++<-]+<++++[>++++++++<-]---<-[>+<-----]++<----[>+<----]--<-[>+<---]<-[>++<-----]++++<-[>+<---]++<-[>+<-------]---<----[>+<----]<--[>+<++++++]<+++++[>+++++<-]++++++
Removes some excess cells then sets indices for the locations of bats
>[[[>]>[>]>>[-<<+>>]<<[<]<[<]>-]>[>]>[>]+++++++[>+++++++++<-]>-[->+>+>+<<<]++++[>>++++<<-]>>+<<<<[<]>[.>]>>[.>]<[<]>[-]>[-]>[-]<+[--<---[->]<]<[->+>+>+<<<]<<[[->>+<<]<]<]
Loops through the indices replacing the spaces at each index with a bat then printing
the set of cells then changing the bat back to spaces


I wanted to use the fact that by adding a newline to the beginning, each stage can be read in both directions to get two different stages, but I couldn't find a good way to do so without generating the full six and a half stages at once.

• Very nice! I don't think I'd have the patience to answer a challenge this big in brainfuck, haha. Oct 21, 2016 at 17:44
• Added a TIO link, hope you don't mind. Also, I seem to count 693 bytes. Oct 21, 2016 at 17:51
• Ungolfed for readability would still win first prize for unreadability :) Total respect for actually doing it in such a crazy language. Very nice and thanks for the answer. +1. Well worth 690 bytes of anyones time. Oct 21, 2016 at 20:54
• @Enigma From me, thanks for the TIO link. Oct 21, 2016 at 21:15
• @DJMcMayhem, I must have messed up the copy/paste, there was a duplicated [<] in the ungolfed form that should have only been there once. Should be fixed now. Oct 21, 2016 at 21:53

# Autovim, 85 81 bytes

No animation, probably still golfable... Still, not bad for the new kid on the block! (autovim)

ñ9am␛ÿP2xÿP4xyjGpđp2o
%ce 15
®q=8j$p ®z=6␍"_d3hP ñğyG12PğÿPC^o^␛v^y6@q==o␛6@z==O  To run it: autovim run ascii-bat.autovim -ni  ## Explanation In short, we draw the moon, copy and paste it 12 times, and use two macros: one to append the bat on the first 7 moons, the other to prepend the last 6. " Draw the moon ñ " Normal mode 9am␛ " Write 9 m ÿP " Duplicate the line 2x " Delete two characters ÿP " Duplicate the line 4x " Delete 4 characters yj " Yank two lines Gp " Paste at the end of the file đp " inverts the current line with the previous 2o " Add two line breaks %ce 15 " Center our moon " Macros ®q=8j$p                           " Macro q jumps 8 lines, and pastes
"   at the end
®z=6␍"_d3hP                      " Macro z jumps 6 lines and replaces
"   the previous 3 characters by the default

" Draw all 12 moons and add the bats
"   register's content
ñ                                 " Run in normal mode
ğyG                              " Copy the moon we just drew
12P                           " Duplicate the moon 12 times
ğÿP                        " Duplicate the top line (gets 3 centered m)
C^o^␛                  " Change the mmm by a bat
v^y               " Copy the bat we just drew
6@q            " Add the first 6 bats
==          " Center the bat
o␛       " Add a line under the bat
6@z    " Add the 6 last bats
==  " Align the last bat in the center
O " Add a line over the last moon

• I followed the download instructions, installed VIM and ended up with the following: VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Dec 9 2014 17:36:41) Unknown option argument: "--not-a-term" More info with: "vim -h" Not got time to look at it now but will have a play this evening. Looks like an interesting project. Oct 18, 2016 at 5:57
• If you have some time to spare, open a GitHub issue - it's working on my phone (Termux, a Linux distribution) as well as on Windows in Git Bash. I'd love to make it work everywhere! My best guess is that VIM compiled in 2013 might really not be the latest version :) (latest version is 8.0) Oct 18, 2016 at 11:14
• OK. I just installed from the package manager so maybe I'll try the upgrade option. I'll let you know what happens. Oct 18, 2016 at 11:18
• Issue raised on GitHub as requested. Please feel free to ignore/reject if you don't consider it a problem since the upgrade resolved it. Oct 18, 2016 at 18:45
• So that means it works now! Great! Oct 18, 2016 at 19:17

## PHP, 167 bytes

I made a small program in PHP:

<?php foreach([0,3,7,9,7,3,0]as$l)$b.=str_pad(str_repeat('=',$l),15," ",2)."\n";foreach([6,25,43,60,75,89,102,83,65,48,33,19,6]as$x)echo substr_replace($b,'^o^',$x,3);


Here is a more verbose version:

// width of the moon
$moonsizes = [0,3,7,9,7,3,0]; // position where to place the bat$positions = [6,25,43,60,75,89,102,83,65,48,33,19,6];
// prepare base moon painting
foreach($moonsizes as$size){
$basepainting .= str_pad(str_repeat('=',$size),15," ",STR_PAD_BOTH)."\n";
}
// loop frames and place bat
foreach($positions as$position) {
echo substr_replace($basepainting,'^o^',$position,3);
}


This is my first codegolf, if you have any suggestion i'm happy to hear :)

• Welcome to PPCG! Nice first answer. Thanks. Oct 19, 2016 at 13:12

## Python 2, 112 bytes

b=['   ','^o^']
for k in range(91):r=k%7;print(b[k/7+r==12]*(r%6>0)+(r*(6-r)*8/5-5)*'m'+b[k/7%12==r]).center(15)


Print the picture. Each line has three parts

• A potential bat on the left
• Some number of m's for the moon
• A potential bat on the right

These parts are concatenated and centered in a box of size 15 for spacing. To avoid the bats shifting the center, a missing bat is three spaces, the same length. For bats on top of or below the moon, the left bar slot is omitted and the right slot is occupied.

There are 91 lines: a 7-line picture for each of 13 pictures. These are counted up via divmod: As k counts from 0 to 91, (k/7, k%7) goes

(0, 0)
(0, 1)
(0, 2)
(0, 3)
(0, 4)
(0, 5)
(0, 6)
(1, 0)
(1, 1)
....
(12, 5)
(12, 6)


Taking the first value k/7 to be the picture and the second value r=k%7 as the row number within the picture, this counts up first by picture, then by row number within each picture, both 0-indexed.

The number of m's in the moon changes with row number r=k%7 as [0,3,7,9,7,3,0]. Rather than indexing into this, a formula was shorter. A convenient degree of freedom is that the 0's can be any negative value, as this still gives the empty string when multiplied by m. Fiddling with a parabola and floor-dividing gave a formula r*(6-r)*8/5-5.

Now, we look at selecting whether to draw a bat or empty space on either side. The array b=[' ','^o^'] contains the options.

The bat in row 0 on in picture 0 (on top), in row 1 in picture 1 (on the right), on to in row 6 in picture 6 (on top). So, it's easy to check whether it appears as the row and picture number as being equal, k/7==r. But we also need picture 12 to look like picture 0, so we take the picture number modulo 12 first.

On the left it's similar. The bat appears on the left in rows r=5,4,3,2,1 in pictures 7,8,9,10,11. So, we check whether the row and picture number sum to 12. We also make sure to draw nothing rather than three spaces on rows 0 and 6 -- the right bat slot will draw the bat, and we must not mess up its centering.

• Any chance of a quick explanation @xnor? I've run it and it sure does work so no explanation needed but if you have time and the inclination it would be interesting for educational purposes :) Oct 18, 2016 at 18:06
• +1 for "Fiddling with a parabola and floor-dividing..." -- if I had to guess, that's the first time that phrase has been used in PPCG... :-) Oct 20, 2016 at 11:24
• Thanks @xnor. Awesome explanation. I am gonna have to spend some time studying this :) Oct 20, 2016 at 21:55

## C#, 615582 337 bytes

This is my first (still far too readable) attempt at one of these, so I'll gladly welcome any suggestions to shave off a few hundred bytes! Top of my list right now is a shorter way to create the moon array.

void M(){string[]m={"","      mmm","    mmmmmmm","   mmmmmmmmm","    mmmmmmm","      mmm",""};var b="^o^";for(int x=0;x<13;x++){var a=(string[])m.Clone();int n=x>6?12-x:x;int[] j={0,1,3,6};a[n]=!a[n].Contains("m")?"      "+b:x<=n?a[n]+b:new string(' ',j[Math.Abs(9-x)])+b+a[n].Replace(" ","");foreach(var c in a){Console.WriteLine(c);}}}


Ungolfed (includes loop!)

class Program {
public static string[] moon = new string[] { "               ", "      mmm      ", "    mmmmmmm    ", "   mmmmmmmmm   ", "    mmmmmmm    ", "      mmm      ", "               " };

public static string bat = "^o^";

static void Main(string[] args) {
while (true) {
Fly();
}
}

static void Fly() {
int times = (moon.Length * 2) - 1;
for (int x = 0; x < times; x++) {
string[] temp = (string[])moon.Clone(); //create a new array to maintain the original

int index = x >= moon.Length ? times - x - 1 : x;

if (!temp[index].Contains("m")) {
temp[index] = new string(' ', 6) + bat + new string(' ', 6);
} else if (x <= index) {
int lastM = temp[index].LastIndexOf('m') + 1;
temp[index] = temp[index].Insert(lastM, bat);
} else {
int firstM = temp[index].IndexOf('m');
char[] src = temp[index].ToCharArray();
int i = firstM - bat.Length;
src[i] = bat[0];
src[i + 1] = bat[1];
src[i + 2] = bat[2];

temp[index] = new string(src);
}

for (int y = 0; y < temp.Length; y++) {
Console.WriteLine(temp[y]);
}
Console.Clear();
}
}
}


Edit:

Took out 21 bytes by removing trailing spaces in the array declaration. Instead of a grid 15 characters wide, each row is only wide enough for the bat to fit. Removed another 12 for the unnecessary string[]args in Main() declaration.

Edit 2:

Rewrote most of the logic, taking out 245 bytes! Includes suggested changes from the comments. Thanks!

From the comments, turned this into a function M() instead of the previous Main() method - so now, this needs to be called externally.

• Welcome to PPCG! :) Oct 18, 2016 at 19:10
• Welcome and nice first effort! Thanks for your answer. Not a c# expert myself so probably can't help you much but I'm sure you'll find many community members who will be happy to help. Oct 18, 2016 at 19:11
• You can save a few bytes by removing the new string[] from m and just putting string[]m={...}. Oct 19, 2016 at 14:22
• Can safe a few more byte by using var's in places such as string[]w=(string[])m.Clone(); - var w=(strin[])m.Clone();
– MX D
Oct 19, 2016 at 14:40
• @ElPedro Sure, I'll take it! It certainly works as a callable function that produces the desired result. Anecdotally, I tried rewriting this again and found a pretty "clever" solution. Much to my chagrin, I ended up +1 from the current byte count, so I think I'm gonna leave it here - thanks for a really fun challenge! This certainly taught me some new things. Oct 19, 2016 at 19:54

# Python 2, 299300290 270 bytes

Golfed down to 270 having gained a bit more golfing experience.

Reference implementation golfed down by 321 320 330 bytes. Not pretty or elegant. Just uses brute force string and list slicing. Was fun to get the byte count down though but I think the approach was completely wrong to start with for a serious competitor.

I don't expect this answer to be taken seriously so please no downvotes. I did say in the question that I would try to golf the reference implementation and this is exactly that. Just posted for fun.

c,e,n=' ','m','\n';f,g=c*9+e*3+c*9,c*7+e*7+c*7;h=9,12;i=12,15;j=6,9;k=13,17;l=4,7;m=c*21,f,g,c*6+e*9+c*6,g,f,c*21;p=h,i,k,(15,18),k,i,h,j,l,(3,6),l,j,h;w=0
for z in map(int,'0123456543210'):print n.join(m[:z])+n+m[z][:p[w][0]]+'^o^'+m[z][p[w][1]:]+n+n.join(m[z+1:]);w+=1


Try it online!

## Ruby, 164 156 bytes

puts Zlib.inflate Base64.decode64 "eNqlkrUVAAEMQvtMcZP9DfLYvzo3qhNSxQ2GVRC1ad29ar1JXaoHbul16Yig+p6BCKqtFBFU+1IE1famoHrX/jZBvN8e8f5tiE94Ib4SBREwNKH5BNJy2QM="


Very simple program. Can be worked on more. Please leave tips in the comments.

• Nice answer. I'm currently playing with Ruby at the moment cos it looks much less verbose than Python when it comes to golfing. Oct 17, 2016 at 19:57
• @ElPedro This is just the text stripped of trailing whitespace, zlib compressed, and base64 encoded. No magic Ruby action anywhere. Oct 17, 2016 at 20:09
• Still think this approach would be a lot more heavy with Python unless someone wants to prove me wrong (@Jonathan perhaps? :)) Oct 17, 2016 at 20:14
• Welp, I forgot to include some libraries and SE won't let me do it. Can someone please include the base64 and zlib libraries in the shortest way possible, and add them to the byte count? Oct 17, 2016 at 20:18
• Is there any difference between puts and say? I'm not familiar with Ruby. Oct 18, 2016 at 4:47

# ///, 205 bytes

/*/\/\///B/^o^*9/
*M/mmm*N/MM*O/   *0/OO*n/

*1/O Nm0
*2/NM0
*3/0M9*4/1O2*7/13n*5/34/0B9570MB0
473O NmBO
O2731ONMBO
75O NmBO
3n510MB0n
5130B9n51OBM9n5 BNm0
3n31B273 BNm0
O27OBM9470B90M0O
410MO


Try it online!

Only if I didn't need tons of trailing spaces...

# Mumps, 223 Bytes

This is using InterSystems Cache Mumps -- it allows braces around loops which make it handy for nested loops in a single line.

S X="09121415141209060403040609",R="097679",L="037973",S=-14 F Y=1:1:13 S S=S+2 F C=1:1:7 S F=$E(X,Y*2-1,Y*2),(M,T)="",$P(T," ",21)="" F A=1:1:$E(L,C){S M=M_"m"} S$E(T,$E(R,C))=M S:C=$S(Y<8:Y,1:Y-S) $E(T,F,F+2)="^o^" W T,!  Could probably be golfed more, and I may play with that when I have more time. Add another 9 bytes, and it'll be animated [[ due to the addition of the 'H 1 W # ' --> that halts for a second and clears the screen: S X="09121415141209060403040609",R="097679",L="037973",S=-14 F Y=1:1:13 S S=S+2 H 1 W # F C=1:1:7 S F=$E(X,Y*2-1,Y*2),(M,T)="",$P(T," ",21)="" F A=1:1:$E(L,C){S M=M_"m"} S $E(T,$E(R,C))=M S:C=$S(Y<8:Y,1:Y-S)$E(T,F,F+2)="^o^" W T,!


Here's an ungolfed / explained version (of the animation version), with the correct 'do' statements and dot loops:

 S X="09121415141209060403040609"
S R="097679",L="037973",S=-14              ; Initialize Variablex
F Y=1:1:13 D                               ; Set up our main loop of 13 cels
. S S=S+2                                  ; set up secondary counter, for start char.
. H 1                                      ; halt for 1 second
. W #                                      ; clear screen
. F C=1:1:7 D                              ; set up our per-line loop
. . S F=$E(X,Y*2-1,Y*2) ; set F to 2-digit "start of bat" . . S (M,T)="" ; set our "builder" lines to empty strings . . S$P(T," ",21)=""                      ; then reset our main 'line string' to 21 spaces.
. . F A=1:1:$E(L,C){S M=M_"m"} ; make our 'moon' text. . . S$E(T,$E(R,C))=M ; and insert our 'moon' into the empty line . . S:C=$S(Y<8:Y,1:Y-S) $E(T,F,F+2)="^o^" ; find the start character for our 'bat' and insert. . . W T,! ; and write the line and a CR/LF.  I tried to do the "compress then Base-64 encode" jazz just to see what would happen, but the encoded string ended up a little longer than the program itself! That, and Cache's system calls for the compression and base64 encoding are quite long... for example, here's the system call to decode base64:$System.Encryption.Base64Decode(STRING)

That method would 'bloat' the program to over 300 characters, I think...