Skip to content

Instantly share code, notes, and snippets.

@sppmacd
Last active February 6, 2024 11:20
Show Gist options
  • Save sppmacd/0a806c65ce634b2825c4016c88b73c73 to your computer and use it in GitHub Desktop.
Save sppmacd/0a806c65ce634b2825c4016c88b73c73 to your computer and use it in GitHub Desktop.
Bad Apple!! in C++ template errors

How to run

  1. Install GCC (tested on 12.2.0) and pyvideoreader (pip3 install pyvideoreader)
  2. Enter target terminal size to terminal_size.txt
  3. Replace line 7 of preprocess.py with:
    video = VideoReader('<path to your video>')

and line 10 of generate_cpp.py with:

        '<path to your video>').number_of_frames)()

(thanks @56independent for a comment!)

  1. Run:
python preprocess.py
python generate_cpp.py
  1. Run with buffering so that animation plays with original 30fps
g++ sources/badapple_*.cpp |& python buffer.py
  1. Or just play (this doesn't keep up speed and passes very fast)
g++ sources/badapple_*.cpp
import os
import sys
import time
terminal_size = (lambda: [int(i) for i in open("terminal_size.txt").read().split()])()
FRAME_TIME = 1/30
while True:
start_time = time.time()
out = ""
for i in range(7):
out += (sys.stdin.readline() or exit())
sys.stdout.write(out or exit())
time.sleep(FRAME_TIME - (time.time() - start_time))
from io import StringIO
import numpy as np
import os
import random
import sys
import time
from videoreader import VideoReader
frame_count = (lambda: VideoReader(
'/home/sppmacd/Videos/badapple.webm').number_of_frames)()
terminal_size = (lambda: [int(i) for i in open("terminal_size.txt").read().split()])()
FUNCTION_NAMES = [
"i",
"up",
"get",
"load",
"parse",
"format"
]
lead_size = len(" 10 | frame< >(\"")
os.makedirs("sources", exist_ok=True)
def rand_op():
chars = "+-*/%"
return chars[random.randrange(len(chars))]
FRAMES_PER_FILE = 200
FRAME_REPETITIONS = 1
for i in range(int(frame_count / FRAMES_PER_FILE) + 1):
with open(f"sources/badapple_{i:03}.txt", "w") as ascii_file:
for j in range(FRAMES_PER_FILE):
frame = i * FRAMES_PER_FILE + j
print(f"Generating: Frame {frame}/{frame_count} ({frame*100/frame_count:.1f}%)")
try:
with open(f"frames/{frame}", "rb") as frame_file:
frame_data = frame_file.read()
except FileNotFoundError:
break
# FANCY
data = StringIO()
offset = 0
while offset < len(frame_data) - 2:
if frame_data[offset] < 10:
data.write(" ")
offset += 1
else:
longest_white_sequence = 0
offset2 = offset
while offset2 < len(frame_data) - 2 and frame_data[offset2] >= 10:
longest_white_sequence += 1
offset2 += 1
trailing_length = len("() + ")
while longest_white_sequence > 0:
required_function_name_length = longest_white_sequence - trailing_length
if required_function_name_length <= 0:
data.write(f" " * longest_white_sequence)
break
elif required_function_name_length < len(FUNCTION_NAMES):
data.write(f"{FUNCTION_NAMES[required_function_name_length - 1]}() {rand_op()} ")
break
else:
length = random.randrange(1, len(FUNCTION_NAMES) + 1)
data.write(f"{FUNCTION_NAMES[length - 1]}() {rand_op()} ")
longest_white_sequence -= length + trailing_length
offset = offset2
for _ in range(FRAME_REPETITIONS):
ascii_file.write(f" frame<{frame:5}>(")
ascii_file.write(" "*(terminal_size[0] - lead_size))
ascii_file.write(data.getvalue())
ascii_file.write("2);\n")
# PLAIN
# ascii_file.write(f" frame<{i:5.0f}>(")
# ascii_file.write(" "*(terminal_size[0] - lead_size))
# for pixel in frame_data:
# ascii_file.write("#" if pixel > 10 else " ")
# ascii_file.write(");\n")
for i in range(int(frame_count / FRAMES_PER_FILE) + 1):
with open(f"sources/badapple_{i:03}.cpp", "w") as source_file:
template = open("template.cpp").read()
template = template.replace("|FRAMES|", open(f"sources/badapple_{i:03}.txt").read())
source_file.write(template)
import numpy as np
import os
import sys
import time
from videoreader import VideoReader
video = VideoReader('/home/sppmacd/Videos/badapple.webm')
frame_count = video.number_of_frames
terminal_size = (lambda: [int(i) for i in open("terminal_size.txt").read().split()])()
x_step = video.frame_width / (terminal_size[0])
y_step = video.frame_height / (terminal_size[1] - 8)
os.makedirs("frames", exist_ok=True)
for frame in video:
print(f"Preprocessing: Frame {video.current_frame_pos}/{frame_count} ({video.current_frame_pos*100/frame_count:.1f}%)")
with open(f"frames/{int(video.current_frame_pos - 1)}", "wb") as image:
# TODO: Interpolate
for scanline_idx in np.arange(0, video.frame_height, y_step):
scanline = frame[int(scanline_idx)]
for pixel_idx in np.arange(0, video.frame_width, x_step):
pixel = scanline[int(pixel_idx)]
image.write(bytes([pixel[0]]))
template<int I>
struct Idx { };
int i();
int up();
int get();
int load();
int parse();
int format();
struct Foo {};
template<class T>
struct InvokeResult { using Type = int; };
template<>
struct InvokeResult<int> {};
template<class T>
using InvokeResultT = typename InvokeResult<T>::Type;
template<int IDX, class T>
auto frame(T) -> InvokeResultT<T> {
}
void bad_apple()
{
|FRAMES|
}
int main()
{
bad_apple();
}
@dy5topian
Copy link

is this still working , am having error with installing pyvideopalyer ?

@sppmacd
Copy link
Author

sppmacd commented Feb 9, 2023

What is your environment (OS, Python version etc)?
Also what error message do you get?

@minhcrafters
Copy link

is this still working , am having error with installing pyvideopalyer ?

i think the author meant pyvideoreader, not pyvideoplayer

@sppmacd
Copy link
Author

sppmacd commented Feb 12, 2023

is this still working , am having error with installing pyvideopalyer ?

i think the author meant pyvideoreader, not pyvideoplayer

yeah my bad, fixed that

@TechStudent10
Copy link

This is neat!

@56independent
Copy link

It seems like this code is inately tied to a specific video on the disk that does not exist. It links to a home folder of a user which does not exist on most systems, never mind the Videos/badapple.webm file.

To fix this, replace line nine of preprocess.py with:

video = VideoReader('./badapple.webm')

and line 10 of generate_cpp.py with:

'./badapple.webm').number_of_frames)()

And now this program should be portable. You just need to put any webm file called badapple.webm within the directory you are executing the scripts from. It should be black and white for the best results.

@sppmacd
Copy link
Author

sppmacd commented Feb 18, 2023

It seems like this code is inately tied to a specific video on the disk that does not exist. It links to a home folder of a user which does not exist on most systems, never mind the Videos/badapple.webm file.

To fix this, replace line nine of preprocess.py with:

video = VideoReader('./badapple.webm')

and line 10 of generate_cpp.py with:

'./badapple.webm').number_of_frames)()

And now this program should be portable. You just need to put any webm file called badapple.webm within the directory you are executing the scripts from. It should be black and white for the best results.

Added this to README, thanks for noting that!

@56independent
Copy link

Added this to README, thanks for noting that!

You're welcome!

@mov-ebx
Copy link

mov-ebx commented Feb 18, 2023

this fire

@SquareMouse
Copy link

This does not appear to work on macs, which sneakily use clang instead of gcc (you can check this with g++ --version)

For example when I run it on my machine the substitution error does not print all of line 28, so no animation for me.

% g++ --std=c++11 sources/badapple_000.cpp  
sources/badapple_000.cpp:28:5: error: no matching function for call to 'frame'
sources/badapple_000.cpp:23:6: note: candidate template ignored: substitution failure [with IDX = 0, T = int]: no type named 'Type' in 'InvokeResult<int>'
auto frame(T) -> InvokeResultT<T> {
     ^

I reproduced this on GodBolt. I found that clang will only print the source if the length of the line that is unable to find a proper substitution is 4096 or less.
https://godbolt.org/z/1dKzPh5Wh

However if you switch to GCC, it'll print significantly longer lines.

Not feeling up for installing GCC for this, and a 64 x 64 resolution rendering isn't the dream. Cool idea though!

@Quidney
Copy link

Quidney commented Feb 20, 2023

I think the lines in the preprocess.py are 7 and 8, not 9 and 10.

@notMONGCHAW
Copy link

@sppmacd what IDE or Terminal are you using to print the outputs
as Ive tried this with vsc and it doesn't seem to work

@sppmacd
Copy link
Author

sppmacd commented Feb 24, 2023

@sppmacd what IDE or Terminal are you using to print the outputs as Ive tried this with vsc and it doesn't seem to work

Konsole, also what's the problem?

@notMONGCHAW
Copy link

@sppmacd what IDE or Terminal are you using to print the outputs as Ive tried this with vsc and it doesn't seem to work

Konsole, also what's the problem?

most of the terminals I'm using is printing new lines pushing the rest of the code up not properly showing the animation
I tried to use ncurses in the header file of your first template file but I seem to have missed something thus asking you for the console that you are using
image

@sppmacd
Copy link
Author

sppmacd commented Feb 24, 2023

Have you entered size of your terminal in terminal_size.txt?

You can get it in python using os.get_terminal_size()

@notMONGCHAW
Copy link

I did everything but it was the rolling text that was the issue on standard terminal
but konsole somehow has a fix for that

@sppmacd
Copy link
Author

sppmacd commented Feb 24, 2023

hmm, strange

@LuxOrions
Copy link

The buffer wont work unless I remove the terminal_size. And I have to re-adjust the terminal_size.txt to my current terminal width x length.

Anyway it works like a charm!!

@notMONGCHAW
Copy link

notMONGCHAW commented Apr 17, 2023

The buffer wont work unless I remove the terminal_size. And I have to re-adjust the terminal_size.txt to my current terminal width x length.
Anyway it works like a charm!!

Yeah the terminal_size.txt had the default width and height of konsole app for Linux
if you are going to use any other app you have to change the size in there with your sizes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment