Skip to content

Commit

Permalink
CSS/HTML improvements and better README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
rafa-br34 committed Apr 25, 2024
1 parent aa625c2 commit f9ab9a0
Show file tree
Hide file tree
Showing 12 changed files with 389 additions and 248 deletions.
18 changes: 18 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "CMake",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"intelliSenseMode": "${default}",
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
}
15 changes: 0 additions & 15 deletions .vscode/launch.json

This file was deleted.

2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"cmake.configureOnOpen": true
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 21 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
![Langton's ant originally on a 30720x17280 grid (resized to 4320x4320) with the LRRRRRLLR pattern after 1292334158 iterations](ASSETS/LRRRRRLLR_30720x17280_1292334158_RESIZED_512x512.png)

This repository contains a C++ and web implementation of the [Langton's ant](https://wikipedia.org/wiki/Langton's_ant) universal Turing machine.

The web version can be found [here](https://rafa-br34.github.io/LangtonsAnt)



![Langton's ant originally on a 30720x17280 grid (resized to 4320x4320) with the LRRRRRLLR pattern after 1292334158 iterations](ASSETS/LRRRRRLLR_30720x17280_1292334158_RESIZED.png)
## Table of contents
* [Implementation notes](#implementation-notes)
* [Usage](#usage)
* [Web version](#web-version)
* [C++ version](#c-version)
* [Arguments](#arguments)
* [`x` & `y`](#x---y)
* [`m`](#m)
* [`a`](#a)
* [`i`](#i)
* [`s`](#s)
* [`o`](#o)
* [`t`](#t)

# Usage
## Implementation notes
# Implementation notes
Both the C++ ([Ant.h](https://github.com/rafa-br34/LangtonsAnt/blob/master/SOURCE/Types/Ant.h)) and JavaScript ([Ant.js](https://github.com/rafa-br34/LangtonsAnt/blob/master/WEBSITE/Scripts/Ant.js)) version share the same implementation concept.
There's support for 8 possible operations, which are:
- R45/L45
- R90/L90 (or R/L)
- R135/L135
- C (continue)
- U (180 degree turn)
## Web
# Usage
## Web version
Placeholder

## C++
## C++ version
### Arguments:
#### `-x` & `-y`:
Canvas width/height
### `-m`:
#### `-m`:
Defines a state machine, can later be used when defining a ant with `-a`. Can be chained using `;`
Example:
`-m LRRRRRLLR;RLLLLLRRL`: Creates two state machines with index 0 and 1
Expand Down Expand Up @@ -51,9 +63,8 @@ Defines how to output images.
- `f:<format>:<name>`: Write image files. `%d` can be used for the image index and `%i` for the current iteration.
- `s:<format>:<stdout|stderr|stream>:<stream_name?>`: Write images to a pipe/stream.
Supported formats:
- `idx`/`u8`: Outputs the raw buffer.
- `idx`: Outputs the raw buffer.
- `rgb24`: Outputs the raw buffer as RGB24.
- `png`: Outputs the raw buffer as PNG.
- `idx-gzip`/`u8-gzip`: Outputs a compressed version of the buffer.
#### `-t`:
Defines how many threads should be used, for now this argument only changes the amount of threads used when encoding images, *eventually* multithreading for the ants will also be added.
260 changes: 260 additions & 0 deletions SOURCE/Encoding.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
#pragma once
#include "Common.h"

#include <condition_variable>
#include <iostream>
#include <future>
#include <vector>
#include <string>
#include <thread>
#include <atomic>
#include <mutex>
#include <cmath>

#include <lodepng.h>

#include "Types/SimulationState.h"
#include "Types/Vector.h"
#include "Types/Ant.h"

namespace Encoding {
union RGBA32 {
uint32_t Color = 0;

struct {
uint8_t R, G, B, A;
} RGBA;

operator uint32_t() const { return Color; }
};

typedef RGBA32(* ColorDescriptor)(size_t);
typedef void(* EncoderCallback)(const uint8_t*, size_t, const Vector2<int>&, unsigned int);

class PaletteManager {
private:
std::vector<RGBA32> m_Palette = {};
ColorDescriptor m_LastProcedure = nullptr;

public:
ColorDescriptor ColoringProcedure = [](size_t Index) -> RGBA32 {
auto I = uint32_t(Index);
XOR_SHIFT32(I);
I *= 0x9E3779B9;
return { ((I & 0x00FF0000) >> 16) | (I & 0x0000FF00) | ((I & 0x000000FF) << 16) | (I & 0xFF000000) };
};

void ResizePalette(size_t StateCount) {
if (m_LastProcedure != ColoringProcedure) { // Reset the cache if the coloring procedure has changed
m_LastProcedure = ColoringProcedure;
m_Palette.clear();
}

size_t CacheSize = m_Palette.size();
if (StateCount <= CacheSize) {
if (CacheSize - StateCount > CacheSize * 2) // Resize the cache if it's more than 2x bigger
m_Palette.resize(StateCount);

return;
}

m_Palette.reserve(StateCount);
for (size_t s = m_Palette.size(); s < StateCount; s++)
m_Palette.push_back(ColoringProcedure(s));
}

INLINE size_t GetSize() const {
return m_Palette.size();
}

INLINE RGBA32 GetColor(size_t Index) {
if (Index + 1 > m_Palette.size())
ResizePalette(Index + 1);

return m_Palette[Index];
}

INLINE RGBA32* GetData() {
return m_Palette.data();
}

INLINE RGBA32 operator[](size_t Index) {
return m_Palette[Index];
}
};

class ThreadManager {
private:
std::condition_variable m_ThreadsSignal = {};
std::atomic<size_t> m_ThreadsActive = 0; // @todo std::atomic might not be required because of m_ThreadsLock and m_ThreadsSignal
std::mutex m_ThreadsLock = {};

public:
size_t ThreadCount = 0;

void AcquireThread() {
std::unique_lock<std::mutex> Lock(m_ThreadsLock);
while (ThreadCount > 0 && m_ThreadsActive >= ThreadCount)
m_ThreadsSignal.wait(Lock);

m_ThreadsActive++;
DEBUG_PRINT("Acquired thread(%d active)\n", (int)m_ThreadsActive);
}

void ReleaseThread() {
std::unique_lock<std::mutex> Lock(m_ThreadsLock);
m_ThreadsActive--;
m_ThreadsSignal.notify_all();
DEBUG_PRINT("Released thread(%d active)\n", (int)m_ThreadsActive);
}

void WaitJobs() {
std::unique_lock<std::mutex> Lock(m_ThreadsLock);
while (m_ThreadsActive > 0)
m_ThreadsSignal.wait(Lock);
}

size_t ActiveThreads() const { return m_ThreadsActive; }

ThreadManager() = default;
~ThreadManager() { WaitJobs(); }
};

enum class PNGFormat : uint8_t {
PNG_PALETTE,
PNG_GRAYSCALE,
_PNG_END,

RAW_U8,
_RAW_END
};

class EncoderState {
private:
lodepng::State m_SetupEncoderState(size_t StateCount, unsigned int BitDepth) {
lodepng::State State = {};

lodepng_state_init(&State);

LodePNGColorType ColorType;

switch (Format) {
case DataFormat::PNG_PALETTE: ColorType = LCT_PALETTE; break;
case DataFormat::PNG_GRAYSCALE: ColorType = LCT_GREY; break;

default: {
DEBUG_PRINT("Unknown data format %d, ColorType will be set to LCT_GREY\n", (int)Format);
ColorType = LCT_GREY;
break;
}
}

State.info_png.color.colortype = ColorType;
State.info_raw.colortype = ColorType;

State.info_png.color.bitdepth = BitDepth;
State.info_raw.bitdepth = BitDepth;

State.encoder.auto_convert = 0;

if (Format != DataFormat::PNG_PALETTE) return State;

// DEBUG_PRINT("Setting up palette with %d colors (%d bits per pixel):\n", (int)StateCount, (int)BitDepth);
Palette.ResizePalette(StateCount);
for (size_t i = 0; i < StateCount; i++) {
auto [R, G, B, A] = Palette[i].RGBA;

// DEBUG_PRINT(" %lu: %06X\n", (long unsigned int)i, Color & 0x00FFFFFF);
lodepng_palette_add(&State.info_png.color, R, G, B, 0xFF);
lodepng_palette_add(&State.info_raw, R, G, B, 0xFF);
}

return State;
}


template<typename Function, typename CellType, typename SizeType>
void m_EncodeBuffer(const CellType* Grid, const Vector2<SizeType>& Size, size_t StateCount, Function Callback) {
if (DataFormat::_PNG_END > Format) {
std::vector<uint8_t> Buffer = {};
lodepng::State State = m_SetupEncoderState(StateCount, 8);

unsigned int Result = lodepng::encode(Buffer, (const unsigned char*)Grid, (unsigned int)Size.X, (unsigned int)Size.Y, State);

ASSERT_MSG(Result == 0, "lodepng::encode -> %d\n", Result);

Callback(Buffer.data(), Buffer.size(), Vector2<int>(Size.X, Size.Y), Result);
}
else if (DataFormat::_RAW_END > Format) {
Callback(Grid, (size_t)Size.X * (size_t)Size.Y, Vector2<int>(Size.X, Size.Y), 0);
}
}


public:
PaletteManager Palette = {};
ThreadManager Threads = {};

DataFormat Format = DataFormat::PNG_PALETTE;

template<typename Function=EncoderCallback, typename CellType, typename SizeType>
void EncodeSync(const SimulationState<CellType, SizeType>& State, Function Callback) {
m_EncodeBuffer(State.CanvasPointer, State.CanvasSize, State.PossibleStates, Callback);
}

template<typename Function=EncoderCallback, typename CellType, typename SizeType>
void EncodeSync(const SimulationState<CellType, SizeType>& State, const Vector2<SizeType>& SectionPosition, const Vector2<SizeType>& SectionSize, Function Callback) {
std::vector<CellType> Section(SectionSize.X * SectionSize.Y);

for (SizeType Y = 0; Y < SectionSize.Y; Y++) {
auto RowStart = State.CanvasPointer + FLATTEN_2D(SectionPosition.X, SectionPosition.Y + Y, State.CanvasSize.X);

std::copy(
RowStart,
RowStart + SectionSize.X,
Section.begin() + SectionSize.Y * Y
);
}

return m_EncodeBuffer(Section.data(), SectionSize, State.PossibleStates, Callback);
}

template<typename Function=EncoderCallback, typename CellType, typename SizeType>
void EncodeAsync(const SimulationState<CellType, SizeType>& State, Function Callback) {
auto GridCopy = std::make_shared<std::vector<CellType>>();
auto GridSize = State.CanvasSize;

GridCopy->assign(State.CanvasPointer, State.CanvasPointer + GridSize.X * GridSize.Y);

Threads.AcquireThread();
std::thread([&, GridCopy, GridSize, States = State.PossibleStates, Callback]() {
m_EncodeBuffer(GridCopy->data(), GridSize, States, Callback);
Threads.ReleaseThread();
}).detach();
}

template<typename Function=EncoderCallback, typename CellType, typename SizeType>
void EncodeAsync(const SimulationState<CellType, SizeType>& State, const Vector2<SizeType>& SectionPosition, const Vector2<SizeType>& SectionSize, Function Callback) {
auto Section = std::make_shared<std::vector<CellType>>(SectionSize.X * SectionSize.Y);

for (SizeType Y = 0; Y < SectionSize.Y; Y++) {
auto RowStart = State.CanvasPointer + FLATTEN_2D(SectionPosition.X, SectionPosition.Y + Y, State.CanvasSize.X);

std::copy(
RowStart,
RowStart + SectionSize.X,
Section->begin() + SectionSize.Y * Y
);
}

Threads.AcquireThread();
std::thread([&, Section, SectionSize, States = State.PossibleStates, Callback]() {
m_EncodeBuffer(Section->data(), SectionSize, States, Callback);
Threads.ReleaseThread();
}).detach();
}

EncoderState() = default;
~EncoderState() { Threads.WaitJobs(); }
};
}
Loading

0 comments on commit f9ab9a0

Please sign in to comment.