Compare commits

..

12 Commits

Author SHA1 Message Date
8232bf66ee Slightly less cantankerous mini TCP echo server 2025-07-07 21:46:40 -04:00
8b39d6cc82 Teensy TCP echo server 2025-07-07 21:23:01 -04:00
0b2d033cd8 Why am I even doing this 2025-07-05 09:12:17 -04:00
f134ca4979 An mess 2025-07-05 07:40:54 -04:00
7fb96a8db7 Remove piston tutorials 2025-02-23 11:44:13 -05:00
cf91122a57 Building some sweep tag I think! 2024-11-30 22:59:33 -05:00
174c101fb1 Such temperature 2024-11-30 19:53:05 -05:00
4e7e47dd94 Doing a cc4e 2024-11-30 15:53:33 -05:00
a5ce30aeeb Up through tutorial part 3, the first bit 2024-09-16 06:56:04 -04:00
11d4baf648 Adding polls app while working through django tutorial 2024-09-15 21:26:11 -04:00
b1197d5c6a Ruffed up 2024-09-14 21:36:07 -04:00
2be95276a5 Oh djoy 2024-09-14 21:34:50 -04:00
42 changed files with 903 additions and 4431 deletions

19
cc4e/fc.c Normal file
View File

@@ -0,0 +1,19 @@
#include <stdio.h>
/* print Fahrenheit-Celsius table
for f = 0, 20, ..., 300 */
int main() {
int lower, upper, step;
float fahr, celsius;
lower = 0; /* lower limit of temperature table */
upper = 300; /* upper limit */
step = 20; /* step size */
fahr = lower;
while (fahr <= upper) {
celsius = (5.0/9.0) * (fahr-32.0);
printf("%4.0f %6.1f\n", fahr, celsius);
fahr = fahr + step;
}
}

12
cc4e/fc_2.c Normal file
View File

@@ -0,0 +1,12 @@
#include <stdio.h>
#define LOWER 0 /* lower limit of table */
#define UPPER 300 /* upper limit */
#define STEP 20 /* step size */
int main() { /* Fahrenheit-Celsius table */
int fahr;
for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)
printf("%4d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
}

5
cc4e/hello.c Normal file
View File

@@ -0,0 +1,5 @@
#include <stdio.h>
int main() {
printf("hello, world\n");
return 0;
}

2
djoy/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.venv/
*.sqlite3

1
djoy/.python-version Normal file
View File

@@ -0,0 +1 @@
3.12.6

0
djoy/djoy/__init__.py Normal file
View File

16
djoy/djoy/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for djoy project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djoy.settings")
application = get_asgi_application()

125
djoy/djoy/settings.py Normal file
View File

@@ -0,0 +1,125 @@
"""
Django settings for djoy project.
Generated by 'django-admin startproject' using Django 5.1.1.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
import os
import pathlib
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = pathlib.Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("DJOY_SECRET_KEY", "django-insecure-0xohno")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"polls.apps.PollsConfig",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "djoy.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "djoy.wsgi.application"
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = "static/"
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

24
djoy/djoy/urls.py Normal file
View File

@@ -0,0 +1,24 @@
"""
URL configuration for djoy project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("polls/", include("polls.urls")),
path("admin/", admin.site.urls),
]

16
djoy/djoy/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for djoy project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djoy.settings")
application = get_wsgi_application()

23
djoy/manage.py Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djoy.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()

0
djoy/polls/__init__.py Normal file
View File

5
djoy/polls/admin.py Normal file
View File

@@ -0,0 +1,5 @@
from django.contrib import admin
from .models import Question
admin.site.register(Question)

6
djoy/polls/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class PollsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "polls"

View File

@@ -0,0 +1,51 @@
# Generated by Django 5.1.1 on 2024-09-16 01:23
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Question",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("question_text", models.CharField(max_length=200)),
("pub_date", models.DateTimeField(verbose_name="date published")),
],
),
migrations.CreateModel(
name="Choice",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("choice_text", models.CharField(max_length=200)),
("votes", models.IntegerField(default=0)),
(
"question",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="polls.question"
),
),
],
),
]

View File

24
djoy/polls/models.py Normal file
View File

@@ -0,0 +1,24 @@
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
def __str__(self):
return self.question_text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text

3
djoy/polls/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

11
djoy/polls/urls.py Normal file
View File

@@ -0,0 +1,11 @@
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("<int:question_id>/", views.detail, name="detail"),
path("<int:question_id>/results/", views.results, name="results"),
path("<int:question_id>/vote/", views.vote, name="vote"),
]

26
djoy/polls/views.py Normal file
View File

@@ -0,0 +1,26 @@
from django.http import HttpResponse
from .models import Question
def index(request) -> HttpResponse:
latest_question_list = Question.objects.order_by("-pub_date")[:5]
output = ", ".join([q.question_text for q in latest_question_list])
return HttpResponse(output.encode())
def detail(request, question_id) -> HttpResponse:
_ = request
return HttpResponse(f"You're looking at question {question_id}.".encode())
def results(request, question_id) -> HttpResponse:
_ = request
return HttpResponse(
f"You're looking at the results of question {question_id}".encode()
)
def vote(request, question_id) -> HttpResponse:
_ = request
return HttpResponse(f"You're voting on question {question_id}".encode())

3
djoy/requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
Django
django-stubs
ruff

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +0,0 @@
[package]
name = "spinning-square"
version = "0.1.0"
authors = [
"Dan Buch <dan@meatballhat.com>",
"TyOverby <ty@pre-alpha.com>",
"Nikita Pekin <contact@nikitapek.in>"
]
[[bin]]
name = "spinning-square"
[dependencies]
piston = "0.53.0"
piston2d-graphics = "0.43.0"
pistoncore-glutin_window = "0.70.1"
piston2d-opengl_graphics = "0.82.0"

View File

@@ -1,70 +0,0 @@
extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate piston;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventSettings, Events};
use piston::input::{RenderArgs, RenderEvent, UpdateArgs, UpdateEvent};
use piston::window::WindowSettings;
pub struct App {
gl: GlGraphics,
rotation: f64,
}
impl App {
fn render(&mut self, args: &RenderArgs) {
use graphics::*;
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
let square = rectangle::square(0.0, 0.0, 50.0);
let rotation = self.rotation;
let (x, y) = (args.window_size[0] / 2.0, args.window_size[1] / 2.0);
self.gl.draw(args.viewport(), |c, gl| {
clear(GREEN, gl);
let transform = c
.transform
.trans(x, y)
.rot_rad(rotation)
.trans(-25.0, -25.0);
rectangle(RED, square, transform, gl);
})
}
fn update(&mut self, args: &UpdateArgs) {
self.rotation += 2.0 * args.dt;
}
}
fn main() {
let opengl = OpenGL::V3_2;
let mut window: Window = WindowSettings::new("spinning-square", [200, 200])
.graphics_api(opengl)
.exit_on_esc(true)
.build()
.unwrap();
let mut app = App {
gl: GlGraphics::new(opengl),
rotation: 0.0,
};
let mut events = Events::new(EventSettings::new());
while let Some(e) = events.next(&mut window) {
if let Some(args) = e.render_args() {
app.render(&args);
}
if let Some(args) = e.update_args() {
app.update(&args);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
[package]
name = "sudoku"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
piston = "0.53.2"
piston2d-graphics = "0.43.0"
piston2d-opengl_graphics = "0.82.0"
pistoncore-glutin_window = "0.70.1"

View File

@@ -1,99 +0,0 @@
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Sans.
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Mono.
Copyright (c) 2014, Telefonica S.A.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -1,155 +0,0 @@
//! Game board logic.
use std::fs::read_to_string;
const SIZE: usize = 9;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Cell {
pub value: u8,
pub loaded: bool,
pub invalid: bool,
}
#[derive(Debug, PartialEq)]
pub struct Gameboard {
pub cells: [[Cell; SIZE]; SIZE],
pub completed: bool,
}
impl Gameboard {
pub fn new() -> Gameboard {
Gameboard {
cells: [[Cell::default(); SIZE]; SIZE],
completed: false,
}
}
pub fn from_cells(cells: [[u8; SIZE]; SIZE]) -> Gameboard {
let mut ret = Gameboard::new();
for (i, row) in cells.iter().enumerate() {
for (j, &col) in row.iter().enumerate() {
ret.cells[i][j] = Cell {
value: col,
loaded: col != 0,
invalid: false,
};
}
}
ret
}
pub fn char(&self, ind: [usize; 2]) -> Option<char> {
Some(match self.cells[ind[1]][ind[0]].value {
1 => '1',
2 => '2',
3 => '3',
4 => '4',
5 => '5',
6 => '6',
7 => '7',
8 => '8',
9 => '9',
_ => return None,
})
}
pub fn set(&mut self, ind: [usize; 2], val: u8) {
if !self.cells[ind[1]][ind[0]].loaded {
self.validate(ind, val);
self.cells[ind[1]][ind[0]].value = val;
}
self.completed = self
.cells
.iter()
.flatten()
.all(|cell| !cell.invalid && cell.value != 0);
}
pub fn load_sdm(filename: &str) -> Self {
let data = read_to_string(filename).expect("failed to read SDM file");
let mut cells = [[Cell::default(); SIZE]; SIZE];
let mut row = 0;
let mut col = 0;
for c in data.chars() {
if col == SIZE {
col = 0;
row += 1;
}
if let Some(v) = c.to_digit(10) {
let value = v as u8;
cells[row][col] = Cell {
value,
loaded: value != 0,
invalid: false,
};
col += 1;
}
}
Self {
cells,
completed: false,
}
}
fn validate(&mut self, ind: [usize; 2], val: u8) {
let [b, a] = ind;
for i in 0..SIZE {
if i == a {
continue;
}
if self.cells[a][i].value == val {
self.cells[a][b].invalid = true;
return;
}
}
for i in 0..SIZE {
if i == b {
continue;
}
if self.cells[i][b].value == val {
self.cells[a][b].invalid = true;
return;
}
}
let (row, col) = (a / 3, b / 3);
for i in 3 * row..3 * row + 3 {
for j in 3 * col..3 * col + 3 {
if i == a && j == b {
continue;
}
if self.cells[i][j].value == val {
self.cells[a][b].invalid = true;
return;
}
}
}
self.cells[a][b].invalid = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_sdm() {
let got = Gameboard::load_sdm("static/puzzle.sdm");
let want = Gameboard::from_cells([
[0, 1, 6, 4, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 9, 0, 0, 0],
[4, 0, 0, 0, 0, 0, 0, 6, 2],
[0, 7, 0, 2, 3, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 3],
[0, 0, 3, 0, 8, 7, 0, 4, 0],
[9, 6, 0, 0, 0, 0, 0, 0, 5],
[0, 0, 0, 8, 0, 0, 0, 0, 7],
[0, 0, 0, 0, 0, 6, 8, 2, 0],
]);
assert_eq!(got, want);
}
}

View File

@@ -1,57 +0,0 @@
//! Gameboard controller
use piston::GenericEvent;
use crate::Gameboard;
pub struct GameboardController {
pub gameboard: Gameboard,
pub selected_cell: Option<[usize; 2]>,
cursor_pos: [f64; 2],
}
impl GameboardController {
pub fn new(gameboard: Gameboard) -> GameboardController {
GameboardController {
gameboard: gameboard,
selected_cell: None,
cursor_pos: [0.0; 2],
}
}
pub fn event<E: GenericEvent>(&mut self, pos: [f64; 2], size: f64, e: &E) {
use piston::input::{Button, Key, MouseButton};
if let Some(pos) = e.mouse_cursor_args() {
self.cursor_pos = pos;
}
if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() {
let x = self.cursor_pos[0] - pos[0];
let y = self.cursor_pos[1] - pos[1];
if x >= 0.0 && x < size && y >= 0.0 && y < size {
let cell_x = (x / size * 9.0) as usize;
let cell_y = (y / size * 9.0) as usize;
self.selected_cell = Some([cell_x, cell_y]);
}
}
if let Some(Button::Keyboard(key)) = e.press_args() {
if let Some(ind) = self.selected_cell {
match key {
Key::D1 => self.gameboard.set(ind, 1),
Key::D2 => self.gameboard.set(ind, 2),
Key::D3 => self.gameboard.set(ind, 3),
Key::D4 => self.gameboard.set(ind, 4),
Key::D5 => self.gameboard.set(ind, 5),
Key::D6 => self.gameboard.set(ind, 6),
Key::D7 => self.gameboard.set(ind, 7),
Key::D8 => self.gameboard.set(ind, 8),
Key::D9 => self.gameboard.set(ind, 9),
_ => {}
}
}
}
}
}

View File

@@ -1,210 +0,0 @@
//! Gameboard view.
use graphics::character::CharacterCache;
use graphics::types::Color;
use graphics::{Context, Graphics};
use crate::gameboard_controller::GameboardController;
pub struct GameboardViewSettings {
pub position: [f64; 2],
pub size: f64,
pub background_color: Color,
pub border_color: Color,
pub board_edge_color: Color,
pub section_edge_color: Color,
pub cell_edge_color: Color,
pub board_edge_radius: f64,
pub section_edge_radius: f64,
pub cell_edge_radius: f64,
pub selected_cell_background_color: Color,
pub text_color: Color,
pub loaded_cell_background_color: Color,
pub invalid_cell_background_color: Color,
pub invalid_selected_cell_background_color: Color,
pub completed_background_color: Color,
}
impl GameboardViewSettings {
pub fn new() -> GameboardViewSettings {
GameboardViewSettings {
position: [10.0; 2],
size: 400.0,
background_color: [0.8, 0.8, 1.0, 1.0],
border_color: [0.0, 0.0, 0.2, 1.0],
board_edge_color: [0.0, 0.0, 0.2, 1.0],
section_edge_color: [0.0, 0.0, 0.2, 1.0],
cell_edge_color: [0.0, 0.0, 0.2, 1.0],
board_edge_radius: 3.0,
section_edge_radius: 2.0,
cell_edge_radius: 1.0,
selected_cell_background_color: [0.9, 0.9, 1.0, 1.0],
text_color: [0.0, 0.0, 0.1, 1.0],
loaded_cell_background_color: [1.0, 1.0, 1.0, 1.0],
invalid_cell_background_color: [1.0, 0.0, 0.0, 1.0],
invalid_selected_cell_background_color: [1.0, 0.0, 0.5, 1.0],
completed_background_color: [0.0, 1.0, 0.0, 1.0],
}
}
}
pub struct GameboardView {
pub settings: GameboardViewSettings,
}
impl GameboardView {
pub fn new(settings: GameboardViewSettings) -> GameboardView {
GameboardView { settings: settings }
}
pub fn draw<G: Graphics, C>(
&self,
controller: &GameboardController,
glyphs: &mut C,
c: &Context,
g: &mut G,
) where
C: CharacterCache<Texture = G::Texture>,
{
use graphics::{Image, Line, Rectangle, Transformed};
let ref settings = self.settings;
let board_rect = [
settings.position[0],
settings.position[1],
settings.size,
settings.size,
];
if controller.gameboard.completed {
Rectangle::new(settings.completed_background_color).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
} else {
Rectangle::new(settings.background_color).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
for i in 0..9 {
for j in 0..9 {
if controller.gameboard.cells[i][j].loaded {
color_cell(
settings,
[j, i],
settings.loaded_cell_background_color,
c,
g,
);
} else if controller.gameboard.cells[i][j].invalid {
color_cell(
settings,
[j, i],
settings.invalid_cell_background_color,
c,
g,
);
}
}
}
if let Some(ind) = controller.selected_cell {
let cell = controller.gameboard.cells[ind[1]][ind[0]];
let color = if !cell.loaded {
if !cell.invalid {
settings.selected_cell_background_color
} else {
settings.invalid_selected_cell_background_color
}
} else {
settings.loaded_cell_background_color
};
color_cell(settings, ind, color, c, g);
}
}
let text_image = Image::new_color(settings.text_color);
let cell_size = settings.size / 9.0;
for j in 0..9 {
for i in 0..9 {
if let Some(ch) = controller.gameboard.char([i, j]) {
let pos = [
settings.position[0] + i as f64 * cell_size + 15.0,
settings.position[1] + j as f64 * cell_size + 34.0,
];
if let Ok(character) = glyphs.character(34, ch) {
let ch_x = pos[0] + character.left();
let ch_y = pos[1] - character.top();
let text_image = text_image.src_rect([
character.atlas_offset[0],
character.atlas_offset[1],
character.atlas_size[0],
character.atlas_size[1],
]);
text_image.draw(
character.texture,
&c.draw_state,
c.transform.trans(ch_x, ch_y),
g,
);
}
}
}
}
let cell_edge = Line::new(settings.cell_edge_color, settings.cell_edge_radius);
let section_edge = Line::new(settings.section_edge_color, settings.section_edge_radius);
for i in 0..9 {
let x = settings.position[0] + i as f64 / 9.0 * settings.size;
let y = settings.position[1] + i as f64 / 9.0 * settings.size;
let x2 = settings.position[0] + settings.size;
let y2 = settings.position[1] + settings.size;
let vline = [x, settings.position[1], x, y2];
let hline = [settings.position[0], y, x2, y];
if (i % 3) == 0 {
section_edge.draw(vline, &c.draw_state, c.transform, g);
section_edge.draw(hline, &c.draw_state, c.transform, g);
} else {
cell_edge.draw(vline, &c.draw_state, c.transform, g);
cell_edge.draw(hline, &c.draw_state, c.transform, g);
}
}
Rectangle::new_border(settings.board_edge_color, settings.board_edge_radius).draw(
board_rect,
&c.draw_state,
c.transform,
g,
);
}
}
fn color_cell<G: Graphics>(
settings: &GameboardViewSettings,
ind: [usize; 2],
color: [f32; 4],
c: &Context,
g: &mut G,
) {
use graphics::Rectangle;
let cell_size = settings.size / 9.0;
let pos = [ind[0] as f64 * cell_size, ind[1] as f64 * cell_size];
let cell_rect = [
settings.position[0] + pos[0],
settings.position[1] + pos[1],
cell_size,
cell_size,
];
Rectangle::new(color).draw(cell_rect, &c.draw_state, c.transform, g);
}

View File

@@ -1,57 +0,0 @@
//////#![deny(missing_docs)]
//! An Sudoko please.
extern crate glutin_window;
use glutin_window::GlutinWindow;
use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings};
use piston::event_loop::{EventSettings, Events};
use piston::{EventLoop, RenderEvent, WindowSettings};
pub use crate::gameboard::Gameboard;
pub use crate::gameboard_controller::GameboardController;
pub use crate::gameboard_view::{GameboardView, GameboardViewSettings};
mod gameboard;
mod gameboard_controller;
mod gameboard_view;
fn main() {
let opengl = OpenGL::V3_2;
let settings = WindowSettings::new("Sudoku", (640, 480))
.exit_on_esc(true)
.graphics_api(opengl)
.vsync(true);
let mut window: GlutinWindow = settings.build().expect("could not create window");
let mut events = Events::new(EventSettings::new().lazy(true));
let mut gl = GlGraphics::new(opengl);
let args: Vec<_> = std::env::args().collect();
let infile = args.get(1).expect("usage: sudoku <sdm-file>");
let gameboard = Gameboard::load_sdm(infile);
let mut gameboard_controller = GameboardController::new(gameboard);
let gameboard_view_settings = GameboardViewSettings::new();
let gameboard_view = GameboardView::new(gameboard_view_settings);
let texture_settings = TextureSettings::new().filter(Filter::Nearest);
let ref mut glyphs = GlyphCache::new("assets/FiraSans-Regular.ttf", (), texture_settings)
.expect("Could not load font");
while let Some(e) = events.next(&mut window) {
gameboard_controller.event(
gameboard_view.settings.position,
gameboard_view.settings.size,
&e,
);
if let Some(args) = e.render_args() {
gl.draw(args.viewport(), |c, g| {
use graphics::clear;
clear([1.0; 4], g);
gameboard_view.draw(&gameboard_controller, glyphs, &c, g);
})
}
}
}

View File

@@ -1 +0,0 @@
517962483236847915498351762371695248654218397829734156765129834142583679983476520

View File

@@ -1 +0,0 @@
016400000200009000400000062070230100100000003003087040960000005000800007000006820

View File

@@ -0,0 +1,7 @@
module git.meatballhat.com/x/box-o-sand/protobuffery
go 1.22
toolchain go1.23.10
require google.golang.org/protobuf v1.36.6

View File

@@ -0,0 +1,6 @@
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=

View File

@@ -0,0 +1,327 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.31.0
// source: addressbook.proto
package __
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type PhoneType int32
const (
PhoneType_PHONE_TYPE_UNSPECIFIED PhoneType = 0
PhoneType_PHONE_TYPE_MOBILE PhoneType = 1
PhoneType_PHONE_TYPE_HOME PhoneType = 2
PhoneType_PHONE_TYPE_WORK PhoneType = 3
)
// Enum value maps for PhoneType.
var (
PhoneType_name = map[int32]string{
0: "PHONE_TYPE_UNSPECIFIED",
1: "PHONE_TYPE_MOBILE",
2: "PHONE_TYPE_HOME",
3: "PHONE_TYPE_WORK",
}
PhoneType_value = map[string]int32{
"PHONE_TYPE_UNSPECIFIED": 0,
"PHONE_TYPE_MOBILE": 1,
"PHONE_TYPE_HOME": 2,
"PHONE_TYPE_WORK": 3,
}
)
func (x PhoneType) Enum() *PhoneType {
p := new(PhoneType)
*p = x
return p
}
func (x PhoneType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (PhoneType) Descriptor() protoreflect.EnumDescriptor {
return file_addressbook_proto_enumTypes[0].Descriptor()
}
func (PhoneType) Type() protoreflect.EnumType {
return &file_addressbook_proto_enumTypes[0]
}
func (x PhoneType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use PhoneType.Descriptor instead.
func (PhoneType) EnumDescriptor() ([]byte, []int) {
return file_addressbook_proto_rawDescGZIP(), []int{0}
}
type Person struct {
state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Id int32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"`
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
Phones []*Person_PhoneNumber `protobuf:"bytes,4,rep,name=phones,proto3" json:"phones,omitempty"`
LastUpdated *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Person) Reset() {
*x = Person{}
mi := &file_addressbook_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Person) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Person) ProtoMessage() {}
func (x *Person) ProtoReflect() protoreflect.Message {
mi := &file_addressbook_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Person.ProtoReflect.Descriptor instead.
func (*Person) Descriptor() ([]byte, []int) {
return file_addressbook_proto_rawDescGZIP(), []int{0}
}
func (x *Person) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Person) GetId() int32 {
if x != nil {
return x.Id
}
return 0
}
func (x *Person) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
func (x *Person) GetPhones() []*Person_PhoneNumber {
if x != nil {
return x.Phones
}
return nil
}
func (x *Person) GetLastUpdated() *timestamppb.Timestamp {
if x != nil {
return x.LastUpdated
}
return nil
}
type AddressBook struct {
state protoimpl.MessageState `protogen:"open.v1"`
People []*Person `protobuf:"bytes,1,rep,name=people,proto3" json:"people,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AddressBook) Reset() {
*x = AddressBook{}
mi := &file_addressbook_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AddressBook) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddressBook) ProtoMessage() {}
func (x *AddressBook) ProtoReflect() protoreflect.Message {
mi := &file_addressbook_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AddressBook.ProtoReflect.Descriptor instead.
func (*AddressBook) Descriptor() ([]byte, []int) {
return file_addressbook_proto_rawDescGZIP(), []int{1}
}
func (x *AddressBook) GetPeople() []*Person {
if x != nil {
return x.People
}
return nil
}
type Person_PhoneNumber struct {
state protoimpl.MessageState `protogen:"open.v1"`
Number string `protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"`
Type PhoneType `protobuf:"varint,2,opt,name=type,proto3,enum=tutorial.PhoneType" json:"type,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Person_PhoneNumber) Reset() {
*x = Person_PhoneNumber{}
mi := &file_addressbook_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Person_PhoneNumber) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Person_PhoneNumber) ProtoMessage() {}
func (x *Person_PhoneNumber) ProtoReflect() protoreflect.Message {
mi := &file_addressbook_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Person_PhoneNumber.ProtoReflect.Descriptor instead.
func (*Person_PhoneNumber) Descriptor() ([]byte, []int) {
return file_addressbook_proto_rawDescGZIP(), []int{0, 0}
}
func (x *Person_PhoneNumber) GetNumber() string {
if x != nil {
return x.Number
}
return ""
}
func (x *Person_PhoneNumber) GetType() PhoneType {
if x != nil {
return x.Type
}
return PhoneType_PHONE_TYPE_UNSPECIFIED
}
var File_addressbook_proto protoreflect.FileDescriptor
const file_addressbook_proto_rawDesc = "" +
"\n" +
"\x11addressbook.proto\x12\btutorial\x1a\x1fgoogle/protobuf/timestamp.proto\"\x87\x02\n" +
"\x06Person\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12\x0e\n" +
"\x02id\x18\x02 \x01(\x05R\x02id\x12\x14\n" +
"\x05email\x18\x03 \x01(\tR\x05email\x124\n" +
"\x06phones\x18\x04 \x03(\v2\x1c.tutorial.Person.PhoneNumberR\x06phones\x12=\n" +
"\flast_updated\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\vlastUpdated\x1aN\n" +
"\vPhoneNumber\x12\x16\n" +
"\x06number\x18\x01 \x01(\tR\x06number\x12'\n" +
"\x04type\x18\x02 \x01(\x0e2\x13.tutorial.PhoneTypeR\x04type\"7\n" +
"\vAddressBook\x12(\n" +
"\x06people\x18\x01 \x03(\v2\x10.tutorial.PersonR\x06people*h\n" +
"\tPhoneType\x12\x1a\n" +
"\x16PHONE_TYPE_UNSPECIFIED\x10\x00\x12\x15\n" +
"\x11PHONE_TYPE_MOBILE\x10\x01\x12\x13\n" +
"\x0fPHONE_TYPE_HOME\x10\x02\x12\x13\n" +
"\x0fPHONE_TYPE_WORK\x10\x03B\x03Z\x01.b\x06proto3"
var (
file_addressbook_proto_rawDescOnce sync.Once
file_addressbook_proto_rawDescData []byte
)
func file_addressbook_proto_rawDescGZIP() []byte {
file_addressbook_proto_rawDescOnce.Do(func() {
file_addressbook_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_addressbook_proto_rawDesc), len(file_addressbook_proto_rawDesc)))
})
return file_addressbook_proto_rawDescData
}
var file_addressbook_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_addressbook_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_addressbook_proto_goTypes = []any{
(PhoneType)(0), // 0: tutorial.PhoneType
(*Person)(nil), // 1: tutorial.Person
(*AddressBook)(nil), // 2: tutorial.AddressBook
(*Person_PhoneNumber)(nil), // 3: tutorial.Person.PhoneNumber
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
}
var file_addressbook_proto_depIdxs = []int32{
3, // 0: tutorial.Person.phones:type_name -> tutorial.Person.PhoneNumber
4, // 1: tutorial.Person.last_updated:type_name -> google.protobuf.Timestamp
1, // 2: tutorial.AddressBook.people:type_name -> tutorial.Person
0, // 3: tutorial.Person.PhoneNumber.type:type_name -> tutorial.PhoneType
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_addressbook_proto_init() }
func file_addressbook_proto_init() {
if File_addressbook_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_addressbook_proto_rawDesc), len(file_addressbook_proto_rawDesc)),
NumEnums: 1,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_addressbook_proto_goTypes,
DependencyIndexes: file_addressbook_proto_depIdxs,
EnumInfos: file_addressbook_proto_enumTypes,
MessageInfos: file_addressbook_proto_msgTypes,
}.Build()
File_addressbook_proto = out.File
file_addressbook_proto_goTypes = nil
file_addressbook_proto_depIdxs = nil
}

View File

@@ -0,0 +1,32 @@
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
option go_package = "git.meatballhat.com/x/box-o-sand/protobuffery/pb";
message Person {
string name = 1;
int32 id = 2;
string email = 3;
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
google.protobuf.Timestamp last_updated = 5;
}
enum PhoneType {
PHONE_TYPE_UNSPECIFIED = 0;
PHONE_TYPE_MOBILE = 1;
PHONE_TYPE_HOME = 2;
PHONE_TYPE_WORK = 3;
}
message AddressBook {
repeated Person people = 1;
}

View File

@@ -0,0 +1,2 @@
ipython
pygame-ce

33
sweeptag/sweeptag.py Normal file
View File

@@ -0,0 +1,33 @@
import pygame
def main():
pygame.init()
screen = pygame.display.set_mode((640, 480))
running = True
font = pygame.Font(None, 80)
title_text = "Sweeptag"
#title_size = font.size(title_text)
title_rendered = font.render(title_text, False, (255, 255, 255), (0, 0, 0))
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
running = False
else:
print(f"EVENT: {event}")
screen.fill((250, 150, 0))
screen.blit(title_rendered, (20, 20))
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,121 @@
package main
import (
"bufio"
"context"
"errors"
"fmt"
"log"
"net"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
)
func main() {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
addr := ":7765"
if len(os.Args) > 1 {
addr = os.Args[1]
}
poolSize := 10
if len(os.Args) > 2 {
if v, err := strconv.ParseInt(os.Args[2], 10, 64); err != nil {
log.Fatal(err)
} else {
poolSize = int(v)
}
}
log.Printf("listening at %v (workers=%v)", addr, poolSize)
listener, err := net.Listen("tcp", addr)
if err != nil {
log.Fatal(err)
}
go func() {
<-ctx.Done()
if err := listener.Close(); err != nil {
log.Printf("failed to close listener: %v", err)
}
}()
workers := []*worker{}
requests := make(chan net.Conn)
for i := 0; i < poolSize; i++ {
w := &worker{id: i, r: requests}
go w.Start(ctx)
workers = append(workers, w)
}
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Microsecond):
continue
default:
log.Printf("entering accept condition")
conn, err := listener.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
return
}
log.Printf("failed to accept connection: %v", err)
continue
}
conn.SetDeadline(time.Now().Add(5 * time.Second))
log.Printf("accepted connection %v", conn.RemoteAddr())
requests <- conn
}
}
}
type worker struct {
id int
r chan net.Conn
}
func (w *worker) Start(ctx context.Context) {
log.Printf("[worker %v] starting loop", w.id)
for {
select {
case <-ctx.Done():
log.Printf("[worker %v] exiting", w.id)
return
case conn := <-w.r:
log.Printf("[worker %v] handling connection %v", w.id, conn.RemoteAddr())
fmt.Fprintf(conn, "ITS ECHO TIME\n")
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
req := strings.TrimSpace(scanner.Text())
if req == "BYE" {
fmt.Fprintf(conn, "BYE BYE NOW\n")
break
}
fmt.Fprintf(conn, "YOU SAID: %v\n", req)
}
if err := conn.Close(); err != nil {
log.Printf("[worker %v] failed to echo: %v", w.id, err)
}
}
}
}

View File

@@ -0,0 +1,3 @@
module git.meatballhat.com/x/box-o-sand/tcp-challenge/server
go 1.21.4