Merge remote-tracking branch 'astro-blog/master'
This commit is contained in:
commit
1d7cf0fbc2
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# jetbrains setting folder
|
||||||
|
.idea/
|
||||||
|
TODO
|
4
.prettierignore
Normal file
4
.prettierignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Ignore artifacts:
|
||||||
|
build
|
||||||
|
coverage
|
||||||
|
public
|
1
.prettierrc
Normal file
1
.prettierrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
4
.vscode/extensions.json
vendored
Normal file
4
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["astro-build.astro-vscode"],
|
||||||
|
"unwantedRecommendations": []
|
||||||
|
}
|
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"command": "./node_modules/.bin/astro dev",
|
||||||
|
"name": "Development server",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"eslint.format.enable": true,
|
||||||
|
"prettier.configPath": ".prettierrc",
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
}
|
||||||
|
}
|
54
README.md
Normal file
54
README.md
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# Astro Starter Kit: Basics
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm create astro@latest -- --template basics
|
||||||
|
```
|
||||||
|
|
||||||
|
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
|
||||||
|
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
|
||||||
|
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)
|
||||||
|
|
||||||
|
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||||
|
|
||||||
|
![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554)
|
||||||
|
|
||||||
|
## 🚀 Project Structure
|
||||||
|
|
||||||
|
Inside of your Astro project, you'll see the following folders and files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/
|
||||||
|
├── public/
|
||||||
|
│ └── favicon.svg
|
||||||
|
├── src/
|
||||||
|
│ ├── components/
|
||||||
|
│ │ └── Card.astro
|
||||||
|
│ ├── layouts/
|
||||||
|
│ │ └── Layout.astro
|
||||||
|
│ └── pages/
|
||||||
|
│ └── index.astro
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||||
|
|
||||||
|
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||||
|
|
||||||
|
Any static assets, like images, can be placed in the `public/` directory.
|
||||||
|
|
||||||
|
## 🧞 Commands
|
||||||
|
|
||||||
|
All commands are run from the root of the project, from a terminal:
|
||||||
|
|
||||||
|
| Command | Action |
|
||||||
|
| :------------------------ | :----------------------------------------------- |
|
||||||
|
| `npm install` | Installs dependencies |
|
||||||
|
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||||
|
| `npm run build` | Build your production site to `./dist/` |
|
||||||
|
| `npm run preview` | Preview your build locally, before deploying |
|
||||||
|
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||||
|
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||||
|
|
||||||
|
## 👀 Want to learn more?
|
||||||
|
|
||||||
|
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
6
astro.config.mjs
Normal file
6
astro.config.mjs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { defineConfig } from "astro/config";
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
integrations: [],
|
||||||
|
});
|
6459
package-lock.json
generated
Normal file
6459
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "log101-blog",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev",
|
||||||
|
"start": "astro dev",
|
||||||
|
"build": "astro check && astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/check": "^0.5.10",
|
||||||
|
"astro": "^4.7.0",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "3.2.5"
|
||||||
|
}
|
||||||
|
}
|
9
public/favicon.svg
Normal file
9
public/favicon.svg
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||||
|
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||||
|
<style>
|
||||||
|
path { fill: #000; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path { fill: #FFF; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 749 B |
61
src/components/Card.astro
Normal file
61
src/components/Card.astro
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { href, title, body } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<li class="link-card">
|
||||||
|
<a href={href}>
|
||||||
|
<h2>
|
||||||
|
{title}
|
||||||
|
<span>→</span>
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
{body}
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<style>
|
||||||
|
.link-card {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
padding: 1px;
|
||||||
|
background-color: #23262d;
|
||||||
|
background-image: none;
|
||||||
|
background-size: 400%;
|
||||||
|
border-radius: 7px;
|
||||||
|
background-position: 100%;
|
||||||
|
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
.link-card > a {
|
||||||
|
width: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding: calc(1.5rem - 1px);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: white;
|
||||||
|
background-color: #23262d;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.link-card:is(:hover, :focus-within) {
|
||||||
|
background-position: 0;
|
||||||
|
background-image: var(--accent-gradient);
|
||||||
|
}
|
||||||
|
.link-card:is(:hover, :focus-within) h2 {
|
||||||
|
color: rgb(var(--accent-light));
|
||||||
|
}
|
||||||
|
</style>
|
8
src/components/Footer.astro
Normal file
8
src/components/Footer.astro
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p class="copyright">© 2024 Furkan Erdem</p>
|
||||||
|
<a class="about-me" href="#">Hakkımda</a>
|
||||||
|
</div>
|
36
src/components/Header.astro
Normal file
36
src/components/Header.astro
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
<ul class="contact-links undecorated-anchor">
|
||||||
|
<li><a href="#">Eposta</a></li>
|
||||||
|
<li><a href="#">Github</a></li>
|
||||||
|
<li><a href="#">LinkedIn</a></li>
|
||||||
|
<li><a href="#">CV</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="title-and-nav">
|
||||||
|
<div class="title-and-gol">
|
||||||
|
<h1 class="main-title">log101</h1>
|
||||||
|
<div id="board" class="board"></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ul class="nav-links undecorated-anchor">
|
||||||
|
<li><a href="#">Teknik</a></li>
|
||||||
|
<li><a href="#">Fikir</a></li>
|
||||||
|
<li><a href="#">Babür'ün Serüvenleri</a></li>
|
||||||
|
<li><a href="#">Ansiklopedi</a></li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConwaySimulator from "../scripts/conway-simulator";
|
||||||
|
|
||||||
|
let gol = new ConwaySimulator(10, 46, 10, 250);
|
||||||
|
document.getElementById("board")?.append(gol.canvas);
|
||||||
|
gol.start();
|
||||||
|
</script>
|
20
src/content/blog/ataturk-ve-demokratik-turkiye.md
Normal file
20
src/content/blog/ataturk-ve-demokratik-turkiye.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
title: "Atatürk ve Demokratik Türkiye"
|
||||||
|
summary: Halil İnalcık'ın kaleminden Türkiye Cumhuriyet'nin kuruluş hikayesi ve Atatürk inkılaplarının toplumdaki akisleri.
|
||||||
|
category: Kitap İncelemesi
|
||||||
|
date: 2024-03-15
|
||||||
|
tags:
|
||||||
|
- kitap
|
||||||
|
- atatürk
|
||||||
|
- inkılap tarihi
|
||||||
|
---
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ut neque sollicitudin, pretium ipsum lacinia, mattis elit. Fusce in dolor suscipit, rutrum risus vehicula, feugiat ipsum. Sed sit amet enim cursus, efficitur eros eu, dapibus lectus. In nec risus eget nisl sodales bibendum nec sit amet nisl. Nulla a dolor finibus, commodo velit et, aliquam lacus. Nunc cursus ultrices tellus, ut volutpat mauris luctus vitae. Donec tristique sollicitudin est, sed iaculis odio eleifend non.
|
||||||
|
|
||||||
|
Fusce posuere non elit in vehicula. Proin facilisis dignissim bibendum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed posuere mi lectus, at eleifend mi tristique eu. Suspendisse potenti. Nam et eros velit.
|
||||||
|
|
||||||
|
Cras viverra sollicitudin neque et bibendum. Pellentesque ultrices mattis venenatis. Nam leo neque, tristique sed arcu vitae, semper rutrum justo. Ut porta orci nec consectetur bibendum. Etiam feugiat pretium lacus, non molestie lorem cursus sed. Nullam a vehicula turpis, nec tristique mauris. Cras euismod, lectus vitae ultricies elementum, ante dolor cursus lorem, at placerat nulla leo eget mauris. Aenean feugiat est ac sem auctor mollis. Maecenas eget interdum leo. Aenean tristique augue sapien, sit amet malesuada nisl molestie sed. Nullam volutpat pellentesque metus, ut mollis odio. Phasellus molestie iaculis lacinia.
|
||||||
|
|
||||||
|
Pellentesque vel massa a ipsum volutpat accumsan in vitae eros. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed porttitor non ligula eget congue. Nunc dictum augue in eros mattis, ut varius risus maximus. Etiam augue tortor, rutrum ac quam vitae, iaculis fringilla lacus. Donec ullamcorper nunc non odio eleifend, vel pulvinar neque fermentum. Cras vel cursus metus. Vestibulum volutpat porta urna, aliquam mollis sem placerat nec. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent eu dictum odio. Nunc eleifend arcu vulputate, volutpat elit ullamcorper, feugiat sapien. Ut egestas aliquet faucibus. Fusce vitae lectus eu nunc mollis aliquet. Fusce cursus imperdiet auctor.
|
||||||
|
|
||||||
|
Vivamus urna arcu, bibendum vitae finibus in, fringilla ut nunc. Praesent fermentum velit non tortor porta, et commodo lectus blandit. Sed non mauris libero. Cras interdum, lorem a dignissim sagittis, diam lorem hendrerit lorem, sit amet porta sapien mauris sed augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin fringilla urna quis tellus hendrerit mattis. Nulla facilisi. In blandit sapien et lacus sollicitudin, ut elementum ex maximus. Integer efficitur libero eleifend luctus rutrum. Aenean a interdum leo, id blandit ante. Nulla velit dolor, euismod nec tempor ut, lobortis sed orci. Ut euismod tempor turpis ac maximus. Pellentesque odio lectus, malesuada a efficitur ac, placerat vel urna.
|
18
src/content/blog/babur-5-bolum-kaptan-ile-mucadele.md
Normal file
18
src/content/blog/babur-5-bolum-kaptan-ile-mucadele.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
title: "5. Bölüm: Kaptan ile Mücadele"
|
||||||
|
summary: "Babür'ün önünde yalnızca tek bir engel kalmıştır: Komutan."
|
||||||
|
category: Öykü
|
||||||
|
date: 2024-03-03
|
||||||
|
tags:
|
||||||
|
- babür
|
||||||
|
---
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ut neque sollicitudin, pretium ipsum lacinia, mattis elit. Fusce in dolor suscipit, rutrum risus vehicula, feugiat ipsum. Sed sit amet enim cursus, efficitur eros eu, dapibus lectus. In nec risus eget nisl sodales bibendum nec sit amet nisl. Nulla a dolor finibus, commodo velit et, aliquam lacus. Nunc cursus ultrices tellus, ut volutpat mauris luctus vitae. Donec tristique sollicitudin est, sed iaculis odio eleifend non.
|
||||||
|
|
||||||
|
Fusce posuere non elit in vehicula. Proin facilisis dignissim bibendum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed posuere mi lectus, at eleifend mi tristique eu. Suspendisse potenti. Nam et eros velit.
|
||||||
|
|
||||||
|
Cras viverra sollicitudin neque et bibendum. Pellentesque ultrices mattis venenatis. Nam leo neque, tristique sed arcu vitae, semper rutrum justo. Ut porta orci nec consectetur bibendum. Etiam feugiat pretium lacus, non molestie lorem cursus sed. Nullam a vehicula turpis, nec tristique mauris. Cras euismod, lectus vitae ultricies elementum, ante dolor cursus lorem, at placerat nulla leo eget mauris. Aenean feugiat est ac sem auctor mollis. Maecenas eget interdum leo. Aenean tristique augue sapien, sit amet malesuada nisl molestie sed. Nullam volutpat pellentesque metus, ut mollis odio. Phasellus molestie iaculis lacinia.
|
||||||
|
|
||||||
|
Pellentesque vel massa a ipsum volutpat accumsan in vitae eros. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed porttitor non ligula eget congue. Nunc dictum augue in eros mattis, ut varius risus maximus. Etiam augue tortor, rutrum ac quam vitae, iaculis fringilla lacus. Donec ullamcorper nunc non odio eleifend, vel pulvinar neque fermentum. Cras vel cursus metus. Vestibulum volutpat porta urna, aliquam mollis sem placerat nec. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent eu dictum odio. Nunc eleifend arcu vulputate, volutpat elit ullamcorper, feugiat sapien. Ut egestas aliquet faucibus. Fusce vitae lectus eu nunc mollis aliquet. Fusce cursus imperdiet auctor.
|
||||||
|
|
||||||
|
Vivamus urna arcu, bibendum vitae finibus in, fringilla ut nunc. Praesent fermentum velit non tortor porta, et commodo lectus blandit. Sed non mauris libero. Cras interdum, lorem a dignissim sagittis, diam lorem hendrerit lorem, sit amet porta sapien mauris sed augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin fringilla urna quis tellus hendrerit mattis. Nulla facilisi. In blandit sapien et lacus sollicitudin, ut elementum ex maximus. Integer efficitur libero eleifend luctus rutrum. Aenean a interdum leo, id blandit ante. Nulla velit dolor, euismod nec tempor ut, lobortis sed orci. Ut euismod tempor turpis ac maximus. Pellentesque odio lectus, malesuada a efficitur ac, placerat vel urna.
|
19
src/content/blog/bir-componentin-seruveni.md
Normal file
19
src/content/blog/bir-componentin-seruveni.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
title: "Bir Komponentin Serüveni: Astro"
|
||||||
|
summary: Yazdığınız komponentlere ne olur? Bu yazıda Astro komponentlerinin serüvenlerine göz atıyoruz!
|
||||||
|
category: Teknik Yazı
|
||||||
|
date: 2024-04-01
|
||||||
|
tags:
|
||||||
|
- astro
|
||||||
|
- javascript
|
||||||
|
---
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ut neque sollicitudin, pretium ipsum lacinia, mattis elit. Fusce in dolor suscipit, rutrum risus vehicula, feugiat ipsum. Sed sit amet enim cursus, efficitur eros eu, dapibus lectus. In nec risus eget nisl sodales bibendum nec sit amet nisl. Nulla a dolor finibus, commodo velit et, aliquam lacus. Nunc cursus ultrices tellus, ut volutpat mauris luctus vitae. Donec tristique sollicitudin est, sed iaculis odio eleifend non.
|
||||||
|
|
||||||
|
Fusce posuere non elit in vehicula. Proin facilisis dignissim bibendum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed posuere mi lectus, at eleifend mi tristique eu. Suspendisse potenti. Nam et eros velit.
|
||||||
|
|
||||||
|
Cras viverra sollicitudin neque et bibendum. Pellentesque ultrices mattis venenatis. Nam leo neque, tristique sed arcu vitae, semper rutrum justo. Ut porta orci nec consectetur bibendum. Etiam feugiat pretium lacus, non molestie lorem cursus sed. Nullam a vehicula turpis, nec tristique mauris. Cras euismod, lectus vitae ultricies elementum, ante dolor cursus lorem, at placerat nulla leo eget mauris. Aenean feugiat est ac sem auctor mollis. Maecenas eget interdum leo. Aenean tristique augue sapien, sit amet malesuada nisl molestie sed. Nullam volutpat pellentesque metus, ut mollis odio. Phasellus molestie iaculis lacinia.
|
||||||
|
|
||||||
|
Pellentesque vel massa a ipsum volutpat accumsan in vitae eros. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed porttitor non ligula eget congue. Nunc dictum augue in eros mattis, ut varius risus maximus. Etiam augue tortor, rutrum ac quam vitae, iaculis fringilla lacus. Donec ullamcorper nunc non odio eleifend, vel pulvinar neque fermentum. Cras vel cursus metus. Vestibulum volutpat porta urna, aliquam mollis sem placerat nec. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent eu dictum odio. Nunc eleifend arcu vulputate, volutpat elit ullamcorper, feugiat sapien. Ut egestas aliquet faucibus. Fusce vitae lectus eu nunc mollis aliquet. Fusce cursus imperdiet auctor.
|
||||||
|
|
||||||
|
Vivamus urna arcu, bibendum vitae finibus in, fringilla ut nunc. Praesent fermentum velit non tortor porta, et commodo lectus blandit. Sed non mauris libero. Cras interdum, lorem a dignissim sagittis, diam lorem hendrerit lorem, sit amet porta sapien mauris sed augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin fringilla urna quis tellus hendrerit mattis. Nulla facilisi. In blandit sapien et lacus sollicitudin, ut elementum ex maximus. Integer efficitur libero eleifend luctus rutrum. Aenean a interdum leo, id blandit ante. Nulla velit dolor, euismod nec tempor ut, lobortis sed orci. Ut euismod tempor turpis ac maximus. Pellentesque odio lectus, malesuada a efficitur ac, placerat vel urna.
|
16
src/content/config.ts
Normal file
16
src/content/config.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { z, defineCollection } from "astro:content";
|
||||||
|
|
||||||
|
const blogCollection = defineCollection({
|
||||||
|
type: "content",
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
tags: z.array(z.string()),
|
||||||
|
summary: z.string(),
|
||||||
|
date: z.date(),
|
||||||
|
category: z.enum(["Kitap İncelemesi", "Teknik Yazı", "Öykü"]),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collections = {
|
||||||
|
blog: blogCollection,
|
||||||
|
};
|
2
src/env.d.ts
vendored
Normal file
2
src/env.d.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/// <reference path="../.astro/types.d.ts" />
|
||||||
|
/// <reference types="astro/client" />
|
41
src/layouts/Layout.astro
Normal file
41
src/layouts/Layout.astro
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="description" content="Astro description" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<meta name="generator" content={Astro.generator} />
|
||||||
|
<title>{title}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<slot />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<style is:global>
|
||||||
|
:root {
|
||||||
|
--h1-desktop: 3.815rem;
|
||||||
|
--h2-desktop: 3.052rem;
|
||||||
|
--h3-desktop: 2.441rem;
|
||||||
|
--h4-desktop: 1.953rem;
|
||||||
|
--h5-desktop: 1.563rem;
|
||||||
|
--h6-desktop: 1.25rem;
|
||||||
|
--small-desktop: 0.8rem;
|
||||||
|
font-size: 20px;
|
||||||
|
font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS",
|
||||||
|
sans-serif;
|
||||||
|
background-color: #f5fffa;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
38
src/pages/index.astro
Normal file
38
src/pages/index.astro
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
import Header from "../components/Header.astro";
|
||||||
|
import Footer from "../components/Footer.astro";
|
||||||
|
import Layout from "../layouts/Layout.astro";
|
||||||
|
import "../styles/header.css";
|
||||||
|
import "../styles/home.css";
|
||||||
|
|
||||||
|
import { getCollection } from "astro:content";
|
||||||
|
|
||||||
|
const allTeknikPosts = await getCollection("blog");
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout title="log101">
|
||||||
|
<div class="container">
|
||||||
|
<Header />
|
||||||
|
<div class="posts">
|
||||||
|
{
|
||||||
|
allTeknikPosts
|
||||||
|
.sort((p1, p2) => p2.data.date.getTime() - p1.data.date.getTime())
|
||||||
|
.map((p) => (
|
||||||
|
<div class="post">
|
||||||
|
<p class="post-category">{p.data.category}</p>
|
||||||
|
<h4 class="post-title">{p.data.title}</h4>
|
||||||
|
<p class="post-date">
|
||||||
|
{p.data.date.toLocaleDateString("tr-TR", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
<p class="post-summary">{p.data.summary}</p>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
</Layout>
|
512
src/scripts/conway-simulator.js
Normal file
512
src/scripts/conway-simulator.js
Normal file
|
@ -0,0 +1,512 @@
|
||||||
|
import { heavyWeightSpaceshipCell } from "./utils";
|
||||||
|
|
||||||
|
/*
|
||||||
|
A wrapper for an HTML <canvas> based visualizatiuon of Conway's Game of Life.
|
||||||
|
*/
|
||||||
|
export default class ConwaySimulator {
|
||||||
|
/*
|
||||||
|
Create a new simulation. A simulation is comprised of a
|
||||||
|
2D data grid (rows-by-cols) of ConwayPixels, a canvas element,
|
||||||
|
and a canvas context.
|
||||||
|
*/
|
||||||
|
constructor(rows, cols, pixelSize, interRoundDelay) {
|
||||||
|
this.rows = rows;
|
||||||
|
this.cols = cols;
|
||||||
|
this.pixelSize = pixelSize;
|
||||||
|
this.interRoundDelay = interRoundDelay;
|
||||||
|
this.mouseIsDown = false;
|
||||||
|
this.paused = false;
|
||||||
|
this.intervalId = null;
|
||||||
|
|
||||||
|
// Make the grid
|
||||||
|
this.grid = [];
|
||||||
|
for (let i = 0; i < rows; i++) {
|
||||||
|
this.grid.push([]);
|
||||||
|
for (let j = 0; j < cols; j++) {
|
||||||
|
let alive = heavyWeightSpaceshipCell(j, i);
|
||||||
|
this.grid[i].push(new ConwayPixel(alive));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inform each pixel who it's neighbors are (performance optimization)
|
||||||
|
for (let i = 0; i < this.rows; i++) {
|
||||||
|
for (let j = 0; j < this.cols; j++) {
|
||||||
|
this.grid[i][j].neighbors = this.getNeighbors(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the canvas
|
||||||
|
let width = this.pixelSize * this.cols;
|
||||||
|
let height = this.pixelSize * this.rows;
|
||||||
|
this.canvas = document.createElement("canvas");
|
||||||
|
this.canvas.width = width;
|
||||||
|
this.canvas.height = height;
|
||||||
|
this.canvasCtx = this.canvas.getContext("2d", { alpha: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Starts the simulation via setInterval if it's not running
|
||||||
|
*/
|
||||||
|
start() {
|
||||||
|
if (this.intervalId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.intervalId = setInterval(() => {
|
||||||
|
this.advanceRound();
|
||||||
|
this.repaint();
|
||||||
|
}, this.interRoundDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the simulation is running, stop it using clearInterval
|
||||||
|
*/
|
||||||
|
stop() {
|
||||||
|
if (this.intervalId) {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
this.intervalId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return the neighbors of a particular grid location
|
||||||
|
*/
|
||||||
|
getNeighbors(row, col) {
|
||||||
|
let neighbors = [];
|
||||||
|
for (let i = row - 1; i <= row + 1; i++) {
|
||||||
|
for (let j = col - 1; j <= col + 1; j++) {
|
||||||
|
if (i === row && j === col) continue;
|
||||||
|
if (this.grid[i] && this.grid[i][j]) {
|
||||||
|
neighbors.push(this.grid[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Update the grid according to the rules for each SimEntity
|
||||||
|
*/
|
||||||
|
advanceRound() {
|
||||||
|
if (this.mouseIsDown) return;
|
||||||
|
|
||||||
|
// First prepare each pixel (give it a next state)
|
||||||
|
for (let i = 0; i < this.rows; i++) {
|
||||||
|
for (let j = 0; j < this.cols; j++) {
|
||||||
|
this.grid[i][j].prepareUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then actually advance them, once all the new states are computed
|
||||||
|
for (let i = 0; i < this.rows; i++) {
|
||||||
|
for (let j = 0; j < this.cols; j++) {
|
||||||
|
this.grid[i][j].update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optimized repaint that only updates pixels that have changed, and paints
|
||||||
|
in batches by color. Using force will repaint all pixels regardless of their
|
||||||
|
state/previousState/nextState, which is slower.
|
||||||
|
*/
|
||||||
|
repaint(force = false) {
|
||||||
|
if (this.mouseIsDown && !force) return;
|
||||||
|
|
||||||
|
// Canvas optimization -- it's faster to paint by color than placement.
|
||||||
|
let byColor = {};
|
||||||
|
for (let i = 0; i < this.rows; i++) {
|
||||||
|
for (let j = 0; j < this.cols; j++) {
|
||||||
|
let pixel = this.grid[i][j];
|
||||||
|
|
||||||
|
if (
|
||||||
|
!force &&
|
||||||
|
!pixel.forceRepaint &&
|
||||||
|
pixel.alive === pixel.previousState
|
||||||
|
) {
|
||||||
|
continue; // No need to repaint if the pixel didn't change
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = pixel.alive ? pixel.lifeStyle : pixel.deathStyle;
|
||||||
|
if (byColor[color] === undefined) {
|
||||||
|
byColor[color] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
byColor[color].push([i, j]);
|
||||||
|
pixel.forceRepaint = false; // Once a pixel is painted, reset it's forced state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let color in byColor) {
|
||||||
|
this.canvasCtx.fillStyle = color;
|
||||||
|
for (let [row, col] of byColor[color]) {
|
||||||
|
this.canvasCtx.fillRect(
|
||||||
|
col * this.pixelSize,
|
||||||
|
row * this.pixelSize,
|
||||||
|
this.pixelSize,
|
||||||
|
this.pixelSize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Paint an individual pixel. This is not used by repaint because of a batch
|
||||||
|
optimziation. painting an individual pixel does take place when click events
|
||||||
|
happen.
|
||||||
|
*/
|
||||||
|
paintPixel(row, col) {
|
||||||
|
this.grid[row][col].setPaintStyles(this.canvasCtx);
|
||||||
|
this.canvasCtx.fillRect(
|
||||||
|
col * this.pixelSize,
|
||||||
|
row * this.pixelSize,
|
||||||
|
this.pixelSize,
|
||||||
|
this.pixelSize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =============
|
||||||
|
Visualizatiuon Modifiers
|
||||||
|
================ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Give each entity in the grid an alive style such that when all pixels are alive
|
||||||
|
the grid would be a rainbow gradient.
|
||||||
|
*/
|
||||||
|
setRainbowScheme() {
|
||||||
|
let rows = this.grid.length;
|
||||||
|
let cols = this.grid[0].length;
|
||||||
|
let diagonalLength = Math.sqrt(
|
||||||
|
this.rows * this.rows + this.cols * this.cols,
|
||||||
|
);
|
||||||
|
let hueIncrement = 360 / diagonalLength;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.rows; i++) {
|
||||||
|
for (let j = 0; j < this.cols; j++) {
|
||||||
|
let h = Math.floor(Math.sqrt(i * i + j * j) * hueIncrement);
|
||||||
|
let px = this.grid[i][j];
|
||||||
|
px.lifeStyle = `hsl(${h}, 100%, 60%)`;
|
||||||
|
px.deathStyle = `#000000`;
|
||||||
|
px.forceRepaint = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Give each entity in the specified area of the grid an alive style
|
||||||
|
such that when all pixels are alive the area would be a rainbow gradient.
|
||||||
|
*/
|
||||||
|
setRainbowSchemeWithin(startRow, stopRow, startCol, stopCol) {
|
||||||
|
let rows = stopRow - startRow;
|
||||||
|
let cols = stopCol - startCol;
|
||||||
|
let diagonalLength = Math.sqrt(rows * rows + cols * cols);
|
||||||
|
let hueIncrement = 360 / diagonalLength;
|
||||||
|
|
||||||
|
for (let i = startRow; i < stopRow; i++) {
|
||||||
|
for (let j = startCol; j < stopCol; j++) {
|
||||||
|
let h = Math.floor(Math.sqrt(i * i + j * j) * hueIncrement);
|
||||||
|
let px = this.grid[i][j];
|
||||||
|
px.lifeStyle = `hsl(${h}, 100%, 60%)`;
|
||||||
|
px.deathStyle = `#000000`;
|
||||||
|
px.forceRepaint = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
set colors to the provided parameters
|
||||||
|
*/
|
||||||
|
setPixelColors(lifeStyle, deathStyle) {
|
||||||
|
this.grid.forEach((row) => {
|
||||||
|
row.forEach((entity) => {
|
||||||
|
entity.lifeStyle = lifeStyle;
|
||||||
|
entity.deathStyle = deathStyle;
|
||||||
|
entity.forceRepaint = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Give the board random semi-complementary colors.
|
||||||
|
*/
|
||||||
|
setRandomPixelColors() {
|
||||||
|
let baseHue = randomInteger(1, 360);
|
||||||
|
let complementaryHue = baseHue + (randomInteger(90, 270) % 360);
|
||||||
|
this.setPixelColors(
|
||||||
|
`hsl(${baseHue}, 100%, 60%)`,
|
||||||
|
`hsl(${complementaryHue}, 100%, 60%)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Given a bounding box, apply the currently selected rules to ONLY the
|
||||||
|
pixels within the provided box.
|
||||||
|
*/
|
||||||
|
applyColorsWithin(
|
||||||
|
rowStart,
|
||||||
|
rowStop,
|
||||||
|
colStart,
|
||||||
|
colStop,
|
||||||
|
lifeStyle,
|
||||||
|
deathStyle,
|
||||||
|
) {
|
||||||
|
for (let i = rowStart; i < rowStop; i++) {
|
||||||
|
for (let j = colStart; j < colStop; j++) {
|
||||||
|
let pixel = this.grid[i][j];
|
||||||
|
pixel.lifeStyle = lifeStyle;
|
||||||
|
pixel.deathStyle = deathStyle;
|
||||||
|
pixel.forceRepaint = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Give a sopecific area of the board random semi-complementary colors.
|
||||||
|
*/
|
||||||
|
applyRandomColorsWithin(rowStart, rowStop, colStart, colStop) {
|
||||||
|
let baseHue = randomInteger(1, 360);
|
||||||
|
let complementaryHue = baseHue + (randomInteger(90, 270) % 360);
|
||||||
|
this.applyColorsWithin(
|
||||||
|
rowStart,
|
||||||
|
rowStop,
|
||||||
|
colStart,
|
||||||
|
`hsl(${baseHue}, 100%, 60%)`,
|
||||||
|
`hsl(${complementaryHue}, 100%, 60%)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Set all the pixels to alive=false
|
||||||
|
*/
|
||||||
|
resetLife(chanceOfLife) {
|
||||||
|
this.grid.forEach((row) => {
|
||||||
|
row.forEach((pixel) => {
|
||||||
|
pixel.previousState = pixel.alive;
|
||||||
|
pixel.alive = Math.random() < chanceOfLife;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Given a bounding box, apply the currently selected rules to ONLY the
|
||||||
|
pixels within the provided box.
|
||||||
|
*/
|
||||||
|
resetLifeWithin(rowStart, rowStop, colStart, colStop, chanceOfLife = 0.1) {
|
||||||
|
for (let i = rowStart; i < rowStop; i++) {
|
||||||
|
for (let j = colStart; j < colStop; j++) {
|
||||||
|
let pixel = this.grid[i][j];
|
||||||
|
if (pixel) {
|
||||||
|
pixel.previousState = pixel.alive;
|
||||||
|
pixel.alive = Math.random() < chanceOfLife;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Update the rules for all the pixels
|
||||||
|
*/
|
||||||
|
setRules(underpopulation, overpopulation, reproductionMin, reproductionMax) {
|
||||||
|
this.grid.forEach((row) => {
|
||||||
|
row.forEach((pixel) => {
|
||||||
|
pixel.underpopulation = underpopulation;
|
||||||
|
pixel.overpopulation = overpopulation;
|
||||||
|
pixel.reproductionMin = reproductionMin;
|
||||||
|
pixel.reproductionMax = reproductionMax;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Swap life and death styles across the center of the grid.
|
||||||
|
*/
|
||||||
|
setYinYangMode() {
|
||||||
|
for (let i = 0; i < this.rows; i++) {
|
||||||
|
for (let j = 0; j < this.cols / 2; j++) {
|
||||||
|
let t = this.grid[i][j].lifeStyle;
|
||||||
|
this.grid[i][j].lifeStyle = this.grid[i][j].deathStyle;
|
||||||
|
this.grid[i][j].deathStyle = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.repaint(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Given a bounding box, apply the currently selected rules to ONLY the
|
||||||
|
pixels within the provided box.
|
||||||
|
*/
|
||||||
|
setRulesWithin(
|
||||||
|
rowStart,
|
||||||
|
rowStop,
|
||||||
|
colStart,
|
||||||
|
colStop,
|
||||||
|
underpopulation,
|
||||||
|
overpopulation,
|
||||||
|
reproductionMin,
|
||||||
|
reproductionMax,
|
||||||
|
) {
|
||||||
|
for (let i = rowStart; i < rowStop; i++) {
|
||||||
|
for (let j = colStart; j < colStop; j++) {
|
||||||
|
let pixel = this.grid[i][j];
|
||||||
|
pixel.underpopulation = underpopulation;
|
||||||
|
pixel.overpopulation = overpopulation;
|
||||||
|
pixel.reproductionMin = reproductionMin;
|
||||||
|
pixel.reproductionMax = reproductionMax;
|
||||||
|
pixel.forceRepaint = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The grid has click, and click-and-drag functionality. Entities define their
|
||||||
|
own behavior when clicked, and this function ensures the proper entity is
|
||||||
|
updated when it is clicked (or dragged-over)
|
||||||
|
*/
|
||||||
|
registerMouseListeners() {
|
||||||
|
bindMultipleEventListener(this.canvas, ["mousemove", "touchmove"], (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.mouseIsDown) {
|
||||||
|
let x, y;
|
||||||
|
if (e.touches) {
|
||||||
|
let rect = e.target.getBoundingClientRect();
|
||||||
|
x = Math.floor((e.touches[0].pageX - rect.left) / this.pixelSize);
|
||||||
|
y = Math.floor((e.touches[0].pageY - rect.top) / this.pixelSize);
|
||||||
|
} else {
|
||||||
|
x = Math.floor(e.offsetX / this.pixelSize);
|
||||||
|
y = Math.floor(e.offsetY / this.pixelSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.grid[y][x].handleClick();
|
||||||
|
this.paintPixel(y, x);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Capture mouse state for click and drag features
|
||||||
|
bindMultipleEventListener(this.canvas, ["mousedown", "touchstart"], (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
let rect = e.target.getBoundingClientRect();
|
||||||
|
let x, y;
|
||||||
|
if (e.touches) {
|
||||||
|
let rect = e.target.getBoundingClientRect();
|
||||||
|
x = Math.floor((e.touches[0].pageX - rect.left) / this.pixelSize);
|
||||||
|
y = Math.floor((e.touches[0].pageY - rect.top) / this.pixelSize);
|
||||||
|
} else {
|
||||||
|
x = Math.floor(e.offsetX / this.pixelSize);
|
||||||
|
y = Math.floor(e.offsetY / this.pixelSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.grid[y][x].handleClick();
|
||||||
|
this.paintPixel(y, x);
|
||||||
|
this.mouseIsDown = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
bindMultipleEventListener(this.canvas, ["mouseup", "touchend"], (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.mouseIsDown = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
A single pixel within a greater ConwaySimulator. Each ConwayPixel has it's own rules for evolution,
|
||||||
|
and for performance reason's maintains a list of it's neighbors inside of it's simulator.
|
||||||
|
|
||||||
|
This class is intended as an internal class, and is not exported. Manipulation of individual
|
||||||
|
ConwayPixels outside of the ConwaySimulator class is not advised.
|
||||||
|
*/
|
||||||
|
class ConwayPixel {
|
||||||
|
/*
|
||||||
|
Constuct a default ConwayPixel, which follows the original Game of Life rules.
|
||||||
|
*/
|
||||||
|
constructor(alive) {
|
||||||
|
this.alive = alive;
|
||||||
|
this.lifeStyle = "#000000";
|
||||||
|
this.deathStyle = "#F5FFFA";
|
||||||
|
this.underpopulation = 2;
|
||||||
|
this.overpopulation = 3;
|
||||||
|
this.reproductionMin = 3;
|
||||||
|
this.reproductionMax = 3;
|
||||||
|
|
||||||
|
// Experimental improvement...
|
||||||
|
this.neighbors = [];
|
||||||
|
this.nextState = null;
|
||||||
|
this.previousState = null;
|
||||||
|
this.forceRepaint = true;
|
||||||
|
|
||||||
|
// Reproduction min cannot be more than reproduction max
|
||||||
|
if (this.reproductionMax < this.reproductionMin) {
|
||||||
|
this.reproductionMin = this.reproductionMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
In order to process whole rounds at a time, update returns
|
||||||
|
a replacement entity, it does not edit the entity in place.
|
||||||
|
|
||||||
|
Following the rules for conway's game of life:
|
||||||
|
Any live cell with fewer than two live neighbors dies,
|
||||||
|
as if caused by underpopulation.
|
||||||
|
|
||||||
|
Any live cell with two or three live neighbors lives on
|
||||||
|
to the next generation.
|
||||||
|
|
||||||
|
Any live cell with more than three live neighbors dies,
|
||||||
|
as if by overpopulation.
|
||||||
|
|
||||||
|
Any dead cell with exactly three live neighbors becomes
|
||||||
|
a live cell, as if by reproduction.
|
||||||
|
|
||||||
|
*/
|
||||||
|
prepareUpdate() {
|
||||||
|
let sum = 0;
|
||||||
|
let nextState = this.alive;
|
||||||
|
|
||||||
|
for (let n of this.neighbors) {
|
||||||
|
if (n.alive && n !== this) sum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextState && sum < this.underpopulation) {
|
||||||
|
nextState = false;
|
||||||
|
} else if (nextState && sum > this.overpopulation) {
|
||||||
|
nextState = false;
|
||||||
|
} else if (
|
||||||
|
!nextState &&
|
||||||
|
sum >= this.reproductionMin &&
|
||||||
|
sum <= this.reproductionMax
|
||||||
|
) {
|
||||||
|
nextState = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nextState = nextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Advance this pixel to it's nextState.
|
||||||
|
*/
|
||||||
|
update() {
|
||||||
|
this.previousState = this.alive;
|
||||||
|
this.alive = this.nextState;
|
||||||
|
this.nextState = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The calling context infers that a click HAS occured, this is not a mouse;
|
||||||
|
this is not an event listener.
|
||||||
|
*/
|
||||||
|
handleClick() {
|
||||||
|
this.alive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Provided with a canvas context, paint ourselves!
|
||||||
|
*/
|
||||||
|
setPaintStyles(canvasCtx) {
|
||||||
|
canvasCtx.fillStyle = this.alive ? this.lifeStyle : this.deathStyle;
|
||||||
|
}
|
||||||
|
}
|
100
src/scripts/gol.js
Normal file
100
src/scripts/gol.js
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
const boardElement = document.getElementById('board');
|
||||||
|
let boardWidth = 100; // Width of the board
|
||||||
|
const boardHeight = 7; // Height of the board
|
||||||
|
const currentGrid = []; // Current state of the grid
|
||||||
|
const nextGrid = []; // Next state of the grid
|
||||||
|
let intervalId; // ID for the setInterval
|
||||||
|
|
||||||
|
const cells = ['30', '20', '51', '01', '62', '63', '03', '64', '54', '44', '34', '24', '14']
|
||||||
|
|
||||||
|
const heavyWeightSpaceshipCell = (x, y) => {
|
||||||
|
const coor = `${x}${y}`
|
||||||
|
return cells.includes(coor)
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeBoard() {
|
||||||
|
for (let y = 0; y < boardHeight; y++) {
|
||||||
|
currentGrid[y] = [];
|
||||||
|
nextGrid[y] = [];
|
||||||
|
for (let x = 0; x < boardWidth; x++) {
|
||||||
|
let cell = document.createElement('div');
|
||||||
|
cell.classList.add('cell');
|
||||||
|
boardElement.appendChild(cell);
|
||||||
|
if (heavyWeightSpaceshipCell(x, y)) {
|
||||||
|
cell.classList.add('alive')
|
||||||
|
currentGrid[y][x] = 1;
|
||||||
|
nextGrid[y][x] = 0;
|
||||||
|
} else {
|
||||||
|
currentGrid[y][x] = 0;
|
||||||
|
nextGrid[y][x] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeNextGeneration() {
|
||||||
|
for (let y = 0; y < boardHeight; y++) {
|
||||||
|
for (let x = 0; x < boardWidth; x++) {
|
||||||
|
const aliveNeighbors = countAliveNeighbors(y, x);
|
||||||
|
const cell = currentGrid[y][x];
|
||||||
|
nextGrid[y][x] = (cell === 1 && (aliveNeighbors === 2 || aliveNeighbors === 3)) || (cell === 0 && aliveNeighbors === 3) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply changes and minimize DOM updates
|
||||||
|
for (let y = 0; y < boardHeight; y++) {
|
||||||
|
for (let x = 0; x < boardWidth; x++) {
|
||||||
|
if (currentGrid[y][x] !== nextGrid[y][x]) {
|
||||||
|
const cellElement = boardElement.children[y * boardWidth + x];
|
||||||
|
cellElement.classList.toggle('alive', nextGrid[y][x] === 1);
|
||||||
|
}
|
||||||
|
currentGrid[y][x] = nextGrid[y][x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function countAliveNeighbors(y, x) {
|
||||||
|
let count = 0;
|
||||||
|
for (let yOffset = -1; yOffset <= 1; yOffset++) {
|
||||||
|
for (let xOffset = -1; xOffset <= 1; xOffset++) {
|
||||||
|
if (yOffset === 0 && xOffset === 0) continue;
|
||||||
|
const newY = (y + yOffset + boardHeight) % boardHeight;
|
||||||
|
const newX = (x + xOffset + boardWidth) % boardWidth;
|
||||||
|
count += currentGrid[newY][newX];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startGame() {
|
||||||
|
if (intervalId) clearInterval(intervalId);
|
||||||
|
intervalId = setInterval(computeNextGeneration, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeWidth() {
|
||||||
|
if (intervalId) clearInterval(intervalId); // stop the game
|
||||||
|
|
||||||
|
document.querySelectorAll(".cell").forEach(el => el.remove()); // remove the cells
|
||||||
|
|
||||||
|
var r = document.querySelector(':root')
|
||||||
|
boardWidth += 50;
|
||||||
|
boardWidth %= 200;
|
||||||
|
|
||||||
|
|
||||||
|
r.style.setProperty('--board-width', boardWidth)
|
||||||
|
initializeBoard()
|
||||||
|
}
|
||||||
|
|
||||||
|
const startButton = document.getElementById('startButton')
|
||||||
|
|
||||||
|
startButton.onclick = startGame
|
||||||
|
|
||||||
|
const changeWidthButton = document.getElementById('changeWidthButton')
|
||||||
|
|
||||||
|
changeWidthButton.onclick = changeWidth
|
||||||
|
|
||||||
|
window.onresize = () => {
|
||||||
|
console.log(document.getElementById('board').clientWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeBoard();
|
6
src/scripts/utils.js
Normal file
6
src/scripts/utils.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const cells = ['30', '20', '51', '01', '62', '63', '03', '64', '54', '44', '34', '24', '14']
|
||||||
|
|
||||||
|
export const heavyWeightSpaceshipCell = (x, y) => {
|
||||||
|
const coor = `${x}${y}`
|
||||||
|
return cells.includes(coor)
|
||||||
|
}
|
87
src/styles/header.css
Normal file
87
src/styles/header.css
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
.container {
|
||||||
|
width: 750px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
margin: 24px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: start;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links a {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.undecorated-anchor a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links a {
|
||||||
|
font-size: var(--h6-desktop);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-and-nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-and-nav hr {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-and-gol {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-family: "Courier New", Courier, monospace;
|
||||||
|
font-size: var(--h1-desktop);
|
||||||
|
max-width: 250;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
color: inherit;
|
||||||
|
}
|
36
src/styles/home.css
Normal file
36
src/styles/home.css
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
.posts {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-category {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: #2f4f4f;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-title {
|
||||||
|
font-size: var(--h4-desktop);
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-summary {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-date {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: gray;
|
||||||
|
}
|
3
tsconfig.json
Normal file
3
tsconfig.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/strict"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user