Compare commits
28 Commits
d4b5c737e5
...
main
Author | SHA1 | Date | |
---|---|---|---|
8232bf66ee
|
|||
8b39d6cc82
|
|||
0b2d033cd8
|
|||
f134ca4979
|
|||
7fb96a8db7
|
|||
cf91122a57
|
|||
174c101fb1
|
|||
4e7e47dd94
|
|||
a5ce30aeeb
|
|||
11d4baf648
|
|||
b1197d5c6a
|
|||
2be95276a5
|
|||
3c8412d858
|
|||
ac113056a8
|
|||
7241713b3f
|
|||
0d19b4cb4f
|
|||
1a0ba35f7a
|
|||
869f273723
|
|||
4c2c6ef429
|
|||
e5166ee912
|
|||
6ea704fe5b
|
|||
01348b1436
|
|||
2dfa546e78
|
|||
224300e1cf
|
|||
0ce1da0737
|
|||
b979a794d6
|
|||
d2d54c12a8
|
|||
8ac30a239d
|
19
cc4e/fc.c
Normal file
19
cc4e/fc.c
Normal 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
12
cc4e/fc_2.c
Normal 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
5
cc4e/hello.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
int main() {
|
||||||
|
printf("hello, world\n");
|
||||||
|
return 0;
|
||||||
|
}
|
2
djoy/.gitignore
vendored
Normal file
2
djoy/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.venv/
|
||||||
|
*.sqlite3
|
1
djoy/.python-version
Normal file
1
djoy/.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.12.6
|
0
djoy/djoy/__init__.py
Normal file
0
djoy/djoy/__init__.py
Normal file
16
djoy/djoy/asgi.py
Normal file
16
djoy/djoy/asgi.py
Normal 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
125
djoy/djoy/settings.py
Normal 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
24
djoy/djoy/urls.py
Normal 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
16
djoy/djoy/wsgi.py
Normal 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
23
djoy/manage.py
Executable 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
0
djoy/polls/__init__.py
Normal file
5
djoy/polls/admin.py
Normal file
5
djoy/polls/admin.py
Normal 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
6
djoy/polls/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PollsConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "polls"
|
51
djoy/polls/migrations/0001_initial.py
Normal file
51
djoy/polls/migrations/0001_initial.py
Normal 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"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
djoy/polls/migrations/__init__.py
Normal file
0
djoy/polls/migrations/__init__.py
Normal file
24
djoy/polls/models.py
Normal file
24
djoy/polls/models.py
Normal 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
3
djoy/polls/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
11
djoy/polls/urls.py
Normal file
11
djoy/polls/urls.py
Normal 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
26
djoy/polls/views.py
Normal 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
3
djoy/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Django
|
||||||
|
django-stubs
|
||||||
|
ruff
|
62
intro-to-algorithms/insertion_sort.go
Normal file
62
intro-to-algorithms/insertion_sort.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
inBytes, err := os.ReadFile(os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
inLines := strings.Split(strings.TrimSpace(string(inBytes)), "\n")
|
||||||
|
inInts := make([]int64, len(inLines))
|
||||||
|
|
||||||
|
for i, line := range inLines {
|
||||||
|
v, err := strconv.ParseInt(line, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
inInts[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := insertionSort(inInts); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, i := range inInts {
|
||||||
|
fmt.Printf("%v\n", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertionSort(inInts []int64) error {
|
||||||
|
if len(inInts) < 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 1; j < len(inInts); j += 1 {
|
||||||
|
key := inInts[j]
|
||||||
|
fmt.Fprintf(os.Stderr, "%+v (save %v from %v)\n", inInts, key, j)
|
||||||
|
|
||||||
|
i := j - 1
|
||||||
|
|
||||||
|
for ; i > -1 && inInts[i] > key; i -= 1 {
|
||||||
|
inInts[i+1] = inInts[i]
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "%+v (j=%v i=%v)\n", inInts, j, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
inInts[i+1] = key
|
||||||
|
fmt.Fprintf(os.Stderr, "%+v (insert %v at %v)\n", inInts, key, i+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
38
intro-to-algorithms/insertion_sort.py
Normal file
38
intro-to-algorithms/insertion_sort.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
in_ints: list[int] = []
|
||||||
|
with open(sys.argv[1]) as infile:
|
||||||
|
for line in infile:
|
||||||
|
in_ints.append(int(line.strip()))
|
||||||
|
|
||||||
|
insertion_sort(in_ints)
|
||||||
|
|
||||||
|
for i in in_ints:
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def insertion_sort(in_ints: list[int]) -> None:
|
||||||
|
for i in range(len(in_ints) - 1):
|
||||||
|
j = i + 1
|
||||||
|
key = in_ints[j]
|
||||||
|
|
||||||
|
print(f"{in_ints} (save {key} from {j})", file=sys.stderr)
|
||||||
|
|
||||||
|
while i > -1 and in_ints[i] > key:
|
||||||
|
in_ints[i + 1] = in_ints[i]
|
||||||
|
|
||||||
|
print(f"{in_ints} (j={j} i={i})", file=sys.stderr)
|
||||||
|
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
in_ints[i + 1] = key
|
||||||
|
print(f"{in_ints} (insert {key} at {i+1})", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
10
intro-to-algorithms/ints00
Normal file
10
intro-to-algorithms/ints00
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
11
|
||||||
|
2
|
||||||
|
17
|
||||||
|
-1
|
||||||
|
34
|
||||||
|
8
|
||||||
|
999
|
||||||
|
2
|
||||||
|
-56
|
||||||
|
3
|
5
lcthw/.ex14.argv
Normal file
5
lcthw/.ex14.argv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
eggs
|
||||||
|
legs
|
||||||
|
pegs
|
||||||
|
jeggings
|
||||||
|
banana pants
|
2
lcthw/.ex17.argv
Normal file
2
lcthw/.ex17.argv
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ex17.db
|
||||||
|
l
|
2
lcthw/.ex17ec.argv
Normal file
2
lcthw/.ex17ec.argv
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ex17ec.db
|
||||||
|
l
|
1
lcthw/.ex17play.argv
Normal file
1
lcthw/.ex17play.argv
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ex17play.bin
|
11
lcthw/.gitignore
vendored
11
lcthw/.gitignore
vendored
@@ -1,9 +1,2 @@
|
|||||||
ex1
|
ex*
|
||||||
ex3
|
!ex*.c
|
||||||
ex7
|
|
||||||
ex8
|
|
||||||
ex9
|
|
||||||
ex10
|
|
||||||
ex11
|
|
||||||
ex12
|
|
||||||
ex13
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
|
||||||
CFLAGS ?= -Wall -g
|
CFLAGS ?= -Wall -g
|
||||||
GDBRUN = gdb --batch --ex run --ex bt --ex q --args
|
GDBRUN = gdb --batch --ex run --ex bt --ex q --args
|
||||||
BUILD_TARGETS = $(foreach ex,$(wildcard ex*.c),$(subst .c,,$(ex)))
|
BUILD_TARGETS = $(foreach ex,$(wildcard ex*.c),$(subst .c,,$(ex)))
|
||||||
@@ -17,19 +19,17 @@ clean:
|
|||||||
build: $(BUILD_TARGETS)
|
build: $(BUILD_TARGETS)
|
||||||
|
|
||||||
.PHONY: gtest
|
.PHONY: gtest
|
||||||
gtest:
|
gtest: $(BUILD_TARGETS)
|
||||||
@$(foreach bt, $(BUILD_TARGETS), make .gtest.$(bt) &&) \
|
@$(foreach bt, $(BUILD_TARGETS), make .gtest.$(bt) &&) printf '\ngYAY\n'
|
||||||
printf '\ngYAY\n'
|
|
||||||
|
|
||||||
.gtest.%:
|
.gtest.%: %
|
||||||
@test_argv=$$(cat .$*.argv 2>/dev/null || echo) && \
|
@if test -f .$*.argv; then readarray -t test_argv <.$*.argv; fi && \
|
||||||
printf '\n==> %s\n' "$*" && $(GDBRUN) ./$*
|
printf '\n==> %s\n' "$*" && $(GDBRUN) ./$* "$${test_argv[@]}"
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test: $(BUILD_TARGETS)
|
||||||
@$(foreach bt, $(BUILD_TARGETS), make .test.$(bt) &&) \
|
@$(foreach bt, $(BUILD_TARGETS), make .test.$(bt) &&) printf '\nYAY\n'
|
||||||
printf '\nYAY\n'
|
|
||||||
|
|
||||||
.test.%:
|
.test.%: %
|
||||||
@test_argv=$$(cat .$*.argv 2>/dev/null || echo) && \
|
@if test -f .$*.argv; then readarray -t test_argv <.$*.argv; fi && \
|
||||||
printf '\n==> %s\n' "$*" && ./$* $$test_argv
|
printf '\n==> %s\n' "$*" && ./$* "$${test_argv[@]}"
|
||||||
|
36
lcthw/ex14.c
Normal file
36
lcthw/ex14.c
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// forward declarations
|
||||||
|
void print_letters(size_t len, char arg[]);
|
||||||
|
|
||||||
|
void print_arguments(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
print_letters(strlen(argv[i]), argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_letters(size_t len, char arg[])
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
char ch = arg[i];
|
||||||
|
|
||||||
|
if (isprint((int)ch)) {
|
||||||
|
printf("'%c' == %d ", ch, ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
print_arguments(argc, argv);
|
||||||
|
return 0;
|
||||||
|
}
|
56
lcthw/ex15.c
Normal file
56
lcthw/ex15.c
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
// create two arrays we care about
|
||||||
|
int ages[] = { 23, 43, 12, 89, 2 };
|
||||||
|
char *names[] = {
|
||||||
|
"Alan", "Frank",
|
||||||
|
"Mary", "John", "Lisa"
|
||||||
|
};
|
||||||
|
|
||||||
|
// safely get the size of ages
|
||||||
|
int count = sizeof(ages) / sizeof(int);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
char **first_name = &names[0];
|
||||||
|
char **last_name = &names[count - 1];
|
||||||
|
|
||||||
|
printf("first_name=%p last_name=%p\n", first_name, last_name);
|
||||||
|
printf("*first_name=%s *last_name=%s\n", *first_name, *last_name);
|
||||||
|
|
||||||
|
printf("---\n");
|
||||||
|
|
||||||
|
// first way using indexing
|
||||||
|
for (i = count - 1; i > -1; i--) {
|
||||||
|
printf("%s has %d years alive.\n", names[i], ages[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("---\n");
|
||||||
|
|
||||||
|
// setup the pointers to the start of the arrays
|
||||||
|
int *cur_age = ages;
|
||||||
|
char **cur_name = names;
|
||||||
|
|
||||||
|
// second way using pointers
|
||||||
|
for (i = count - 1; i > -1; i--) {
|
||||||
|
printf("%s is %d years old.\n", *(cur_name + i), *(cur_age + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("---\n");
|
||||||
|
|
||||||
|
// third way, pointers are just arrays
|
||||||
|
for (i = count - 1; i > -1; i--) {
|
||||||
|
printf("%s is %d years old again.\n", cur_name[i], cur_age[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("---\n");
|
||||||
|
|
||||||
|
// fourth way with pointers in a stupid complex way
|
||||||
|
for (cur_name = names, cur_age = ages;
|
||||||
|
(cur_age - ages) < count; cur_name++, cur_age++) {
|
||||||
|
printf("%s lived %d years so far.\n", *cur_name, *cur_age);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
73
lcthw/ex16.c
Normal file
73
lcthw/ex16.c
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct Person {
|
||||||
|
char *name;
|
||||||
|
int age;
|
||||||
|
int height;
|
||||||
|
int weight;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Person *Person_create(char *name, int age, int height, int weight)
|
||||||
|
{
|
||||||
|
struct Person *who = malloc(sizeof(struct Person));
|
||||||
|
assert(who != NULL);
|
||||||
|
|
||||||
|
who->name = strdup(name);
|
||||||
|
who->age = age;
|
||||||
|
who->height = height;
|
||||||
|
who->weight = weight;
|
||||||
|
|
||||||
|
return who;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Person_destroy(struct Person *who)
|
||||||
|
{
|
||||||
|
assert(who != NULL);
|
||||||
|
|
||||||
|
free(who->name);
|
||||||
|
free(who);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Person_print(struct Person *who)
|
||||||
|
{
|
||||||
|
assert(who != NULL);
|
||||||
|
|
||||||
|
printf("Name: %s\n", who->name);
|
||||||
|
printf("\tAge: %d\n", who->age);
|
||||||
|
printf("\tHeight: %d\n", who->height);
|
||||||
|
printf("\tWeight: %d\n", who->weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
// make two people structures
|
||||||
|
struct Person *joe = Person_create("Joe Alex", 32, 64, 100);
|
||||||
|
|
||||||
|
struct Person *frank = Person_create("Frank Blank", 20, 72, 180);
|
||||||
|
|
||||||
|
// print them out and where they are in memory
|
||||||
|
printf("Joe is at memory location %p:\n", joe);
|
||||||
|
Person_print(joe);
|
||||||
|
|
||||||
|
printf("Frank is at memory location %p:\n", frank);
|
||||||
|
Person_print(frank);
|
||||||
|
|
||||||
|
// make everyone age 20 years and print them again
|
||||||
|
joe->age += 20;
|
||||||
|
joe->height -= 2;
|
||||||
|
joe->weight += 40;
|
||||||
|
Person_print(joe);
|
||||||
|
|
||||||
|
frank->age += 20;
|
||||||
|
frank->weight += 20;
|
||||||
|
Person_print(frank);
|
||||||
|
|
||||||
|
// destroy them both so we clean up
|
||||||
|
Person_destroy(joe);
|
||||||
|
Person_destroy(frank);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
57
lcthw/ex16ec.c
Normal file
57
lcthw/ex16ec.c
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct Person {
|
||||||
|
char *name;
|
||||||
|
int age;
|
||||||
|
int height;
|
||||||
|
int weight;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Person Person_create(char *name, int age, int height, int weight)
|
||||||
|
{
|
||||||
|
struct Person who;
|
||||||
|
|
||||||
|
who.name = strdup(name);
|
||||||
|
who.age = age;
|
||||||
|
who.height = height;
|
||||||
|
who.weight = weight;
|
||||||
|
|
||||||
|
return who;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Person_print(struct Person who)
|
||||||
|
{
|
||||||
|
printf("Name: %s\n", who.name);
|
||||||
|
printf("\tAge: %d\n", who.age);
|
||||||
|
printf("\tHeight: %d\n", who.height);
|
||||||
|
printf("\tWeight: %d\n", who.weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
// make two people structures
|
||||||
|
struct Person joe = Person_create("Joe Alex", 32, 64, 100);
|
||||||
|
|
||||||
|
struct Person frank = Person_create("Frank Blank", 20, 72, 180);
|
||||||
|
|
||||||
|
// print them out and where they are in memory
|
||||||
|
printf("Joe is at memory location %p:\n", &joe);
|
||||||
|
Person_print(joe);
|
||||||
|
|
||||||
|
printf("Frank is at memory location %p:\n", &frank);
|
||||||
|
Person_print(frank);
|
||||||
|
|
||||||
|
// make everyone age 20 years and print them again
|
||||||
|
joe.age += 20;
|
||||||
|
joe.height -= 2;
|
||||||
|
joe.weight += 40;
|
||||||
|
Person_print(joe);
|
||||||
|
|
||||||
|
frank.age += 20;
|
||||||
|
frank.weight += 20;
|
||||||
|
Person_print(frank);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
237
lcthw/ex17.c
Normal file
237
lcthw/ex17.c
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAX_DATA 512
|
||||||
|
#define MAX_ROWS 100
|
||||||
|
|
||||||
|
struct Address {
|
||||||
|
int id;
|
||||||
|
int set;
|
||||||
|
char name[MAX_DATA];
|
||||||
|
char email[MAX_DATA];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Database {
|
||||||
|
struct Address rows[MAX_ROWS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Connection {
|
||||||
|
FILE *file;
|
||||||
|
struct Database *db;
|
||||||
|
};
|
||||||
|
|
||||||
|
void die(const char *message)
|
||||||
|
{
|
||||||
|
if (errno) {
|
||||||
|
perror(message);
|
||||||
|
} else {
|
||||||
|
printf("ERROR: %s\n", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address_print(struct Address *addr)
|
||||||
|
{
|
||||||
|
printf("%d %s %s\n", addr->id, addr->name, addr->email);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_load(struct Connection *conn)
|
||||||
|
{
|
||||||
|
int rc = fread(conn->db, sizeof(struct Database), 1, conn->file);
|
||||||
|
if (rc != 1) {
|
||||||
|
die("Failed to load database.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Connection *Database_open(const char *filename, char mode)
|
||||||
|
{
|
||||||
|
struct Connection *conn = malloc(sizeof(struct Connection));
|
||||||
|
|
||||||
|
if (!conn) {
|
||||||
|
die("Memory error");
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->db = malloc(sizeof(struct Database));
|
||||||
|
if (!conn->db) {
|
||||||
|
die("Memory error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == 'c') {
|
||||||
|
conn->file = fopen(filename, "w");
|
||||||
|
} else {
|
||||||
|
conn->file = fopen(filename, "r+");
|
||||||
|
|
||||||
|
if (conn->file) {
|
||||||
|
Database_load(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn->file) {
|
||||||
|
die("Failed to open file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_close(struct Connection *conn)
|
||||||
|
{
|
||||||
|
if (conn) {
|
||||||
|
if (conn->file) {
|
||||||
|
fclose(conn->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->db) {
|
||||||
|
free(conn->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_write(struct Connection *conn)
|
||||||
|
{
|
||||||
|
rewind(conn->file);
|
||||||
|
|
||||||
|
int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
|
||||||
|
if (rc != 1) {
|
||||||
|
die("Failed to write database.");
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = fflush(conn->file);
|
||||||
|
if (rc == -1) {
|
||||||
|
die("Cannot flush database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_create(struct Connection *conn)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_ROWS; i++) {
|
||||||
|
struct Address addr = {.id = i, .set = 0};
|
||||||
|
conn->db->rows[i] = addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_set(struct Connection *conn, int id, const char *name, const char *email)
|
||||||
|
{
|
||||||
|
struct Address *addr = &conn->db->rows[id];
|
||||||
|
if (addr->set) {
|
||||||
|
die("Already set, delete it first");
|
||||||
|
}
|
||||||
|
|
||||||
|
addr->set = 1;
|
||||||
|
|
||||||
|
char *res = strncpy(addr->name, name, MAX_DATA);
|
||||||
|
if (!res) {
|
||||||
|
die("Name copy failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
addr->name[MAX_DATA - 1] = '\0';
|
||||||
|
|
||||||
|
res = strncpy(addr->email, email, MAX_DATA);
|
||||||
|
if (!res) {
|
||||||
|
die("Email copy failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
addr->email[MAX_DATA - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_get(struct Connection *conn, int id)
|
||||||
|
{
|
||||||
|
struct Address *addr = &conn->db->rows[id];
|
||||||
|
|
||||||
|
if (addr->set) {
|
||||||
|
Address_print(addr);
|
||||||
|
} else {
|
||||||
|
die("ID is not set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_delete(struct Connection *conn, int id)
|
||||||
|
{
|
||||||
|
struct Address addr = {.id = id, .set = 0};
|
||||||
|
conn->db->rows[id] = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_list(struct Connection *conn)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
struct Database *db = conn->db;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_ROWS; i++) {
|
||||||
|
struct Address *cur = &db->rows[i];
|
||||||
|
|
||||||
|
if (cur->set) {
|
||||||
|
Address_print(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc < 3) {
|
||||||
|
die("USAGE: ex17 <dbfile> <action> [action params]");
|
||||||
|
}
|
||||||
|
|
||||||
|
char *filename = argv[1];
|
||||||
|
char action = argv[2][0];
|
||||||
|
struct Connection *conn = Database_open(filename, action);
|
||||||
|
int id = 0;
|
||||||
|
|
||||||
|
if (argc > 3) {
|
||||||
|
id = atoi(argv[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= MAX_ROWS) {
|
||||||
|
die("There's not that many records.");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'c':
|
||||||
|
Database_create(conn);
|
||||||
|
Database_write(conn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g':
|
||||||
|
if (argc != 4) {
|
||||||
|
die("Need an id to get");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database_get(conn, id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
if (argc != 6) {
|
||||||
|
die("Need an id, name, email to set");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database_set(conn, id, argv[4], argv[5]);
|
||||||
|
Database_write(conn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
if (argc != 4) {
|
||||||
|
die("Need an id to delete");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database_delete(conn, id);
|
||||||
|
Database_write(conn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
Database_list(conn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
die("Invalid action: c=create, g=get, s=set, d=del, l=list");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database_close(conn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
305
lcthw/ex17ec.c
Normal file
305
lcthw/ex17ec.c
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define DEFAULT_MAX_DATA 512
|
||||||
|
#define DEFAULT_MAX_ROWS 100
|
||||||
|
|
||||||
|
struct Address {
|
||||||
|
char *name;
|
||||||
|
char *email;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TableOfContents {
|
||||||
|
size_t n_rows;
|
||||||
|
size_t string_field_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Database {
|
||||||
|
struct Address *rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Connection {
|
||||||
|
FILE *file;
|
||||||
|
struct TableOfContents *toc;
|
||||||
|
struct Database *db;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Database_close(struct Connection *conn);
|
||||||
|
struct Address *Database_get(struct Connection *conn, const char *email);
|
||||||
|
|
||||||
|
void die(struct Connection *conn, const char *message)
|
||||||
|
{
|
||||||
|
Database_close(conn);
|
||||||
|
|
||||||
|
if (errno) {
|
||||||
|
perror(message);
|
||||||
|
} else {
|
||||||
|
printf("ERROR: %s\n", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address_print(struct Address *addr)
|
||||||
|
{
|
||||||
|
if (!addr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s %s\n", addr->name, addr->email);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Database_addr_size(struct Connection *conn)
|
||||||
|
{
|
||||||
|
return sizeof(struct Address) +
|
||||||
|
/* name */ conn->toc->string_field_size +
|
||||||
|
/* email */ conn->toc->string_field_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Database_size(struct Connection *conn)
|
||||||
|
{
|
||||||
|
return sizeof(struct Database) +
|
||||||
|
(conn->toc->n_rows * Database_addr_size(conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Database_n_rows(struct Connection *conn)
|
||||||
|
{
|
||||||
|
return sizeof(conn->db->rows) / Database_addr_size(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_load(struct Connection *conn)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
rc = fread(conn->toc, sizeof(struct TableOfContents), 1, conn->file);
|
||||||
|
if (rc != 1) {
|
||||||
|
die(conn, "Failed to load TableOfContents.");
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = fread(conn->db, Database_size(conn), 1, conn->file);
|
||||||
|
if (rc != 1) {
|
||||||
|
die(conn, "Failed to load Database.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Connection *Database_open(const char *filename, char mode, size_t string_field_size)
|
||||||
|
{
|
||||||
|
struct Connection *conn = malloc(sizeof(struct Connection));
|
||||||
|
|
||||||
|
if (!conn) {
|
||||||
|
die(conn, "Memory error allocating Connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->toc = malloc(sizeof(struct TableOfContents));
|
||||||
|
if (!conn->toc) {
|
||||||
|
die(conn, "Memory error allocating TableOfContents");
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->toc->string_field_size = string_field_size;
|
||||||
|
|
||||||
|
conn->db = malloc(sizeof(struct Database));
|
||||||
|
if (!conn->db) {
|
||||||
|
die(conn, "Memory error allocating Database");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == 'c') {
|
||||||
|
conn->file = fopen(filename, "w");
|
||||||
|
|
||||||
|
conn->toc->n_rows = 1;
|
||||||
|
|
||||||
|
conn->db->rows = malloc(Database_addr_size(conn));
|
||||||
|
if (!conn->db->rows) {
|
||||||
|
die(conn, "Memory error allocating Database rows");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
conn->file = fopen(filename, "r+");
|
||||||
|
|
||||||
|
if (conn->file) {
|
||||||
|
Database_load(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn->file) {
|
||||||
|
die(conn, "Failed to open file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_close(struct Connection *conn)
|
||||||
|
{
|
||||||
|
if (conn) {
|
||||||
|
if (conn->file) {
|
||||||
|
fclose(conn->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->toc) {
|
||||||
|
free(conn->toc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->db) {
|
||||||
|
free(conn->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_write(struct Connection *conn)
|
||||||
|
{
|
||||||
|
rewind(conn->file);
|
||||||
|
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
rc = fwrite(conn->toc, sizeof(struct TableOfContents), 1, conn->file);
|
||||||
|
if (rc != 1) {
|
||||||
|
die(conn, "Failed to write TableOfContents.");
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
|
||||||
|
if (rc != 1) {
|
||||||
|
die(conn, "Failed to write Database.");
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = fflush(conn->file);
|
||||||
|
if (rc == -1) {
|
||||||
|
die(conn, "Cannot flush");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_set(struct Connection *conn, const char *name, const char *email)
|
||||||
|
{
|
||||||
|
struct Address *addr = Database_get(conn, email);
|
||||||
|
if (!addr) {
|
||||||
|
addr = malloc(Database_addr_size(conn));
|
||||||
|
conn->db->rows[Database_n_rows(conn)+1] = *addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *res = strncpy(addr->name, name, conn->toc->string_field_size);
|
||||||
|
if (!res) {
|
||||||
|
die(conn, "Name copy failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
addr->name[conn->toc->string_field_size - 1] = '\0';
|
||||||
|
|
||||||
|
res = strncpy(addr->email, email, conn->toc->string_field_size);
|
||||||
|
if (!res) {
|
||||||
|
die(conn, "Email copy failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
addr->email[conn->toc->string_field_size - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Address *Database_get(struct Connection *conn, const char *email)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
struct Address *addr;
|
||||||
|
|
||||||
|
for (i = 0; i < Database_n_rows(conn); i++) {
|
||||||
|
addr = &conn->db->rows[i];
|
||||||
|
|
||||||
|
if (strcmp(addr->email, email) == 0) {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_delete(struct Connection *conn, const char *email)
|
||||||
|
{
|
||||||
|
struct Address *addr = Database_get(conn, email);
|
||||||
|
if (!addr) {
|
||||||
|
// already deleted
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr->email = NULL;
|
||||||
|
addr->name = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Database_list(struct Connection *conn)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
struct Database *db = conn->db;
|
||||||
|
|
||||||
|
for (i = 0; i < Database_n_rows(conn); i++) {
|
||||||
|
struct Address *cur = &db->rows[i];
|
||||||
|
|
||||||
|
Address_print(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_string_field_size()
|
||||||
|
{
|
||||||
|
size_t string_field_size = DEFAULT_MAX_DATA;
|
||||||
|
|
||||||
|
const char *string_field_size_string = getenv("EX17_MAX_DATA");
|
||||||
|
if (string_field_size_string) {
|
||||||
|
int string_field_size_int = atoi(string_field_size_string);
|
||||||
|
string_field_size = string_field_size_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string_field_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc < 3) {
|
||||||
|
die(NULL, "USAGE: ex17 <dbfile> <action> [action params]");
|
||||||
|
}
|
||||||
|
|
||||||
|
char *filename = argv[1];
|
||||||
|
char action = argv[2][0];
|
||||||
|
|
||||||
|
size_t string_field_size = get_string_field_size();
|
||||||
|
|
||||||
|
struct Connection *conn = Database_open(filename, action, string_field_size);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'c':
|
||||||
|
Database_write(conn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g':
|
||||||
|
if (argc != 4) {
|
||||||
|
die(conn, "Need an `email` to get");
|
||||||
|
}
|
||||||
|
|
||||||
|
Address_print(Database_get(conn, argv[3]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
if (argc != 5) {
|
||||||
|
die(conn, "Need a `name` and `email` to set");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database_set(conn, argv[3], argv[4]);
|
||||||
|
Database_write(conn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
if (argc != 4) {
|
||||||
|
die(conn, "Need an `email` to delete");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database_delete(conn, argv[3]);
|
||||||
|
Database_write(conn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
Database_list(conn);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
die(conn, "Invalid action: c=create, g=get, s=set, d=del, l=list");
|
||||||
|
}
|
||||||
|
|
||||||
|
Database_close(conn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
109
lcthw/ex17play.c
Normal file
109
lcthw/ex17play.c
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include <err.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define NAME_SIZE 64
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
int counter;
|
||||||
|
time_t updated_at;
|
||||||
|
char name[NAME_SIZE];
|
||||||
|
size_t history_entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *names[] = {
|
||||||
|
"boingo",
|
||||||
|
"barko",
|
||||||
|
"flip",
|
||||||
|
"dingles",
|
||||||
|
"zebra",
|
||||||
|
"horus",
|
||||||
|
"blendy",
|
||||||
|
"kitems",
|
||||||
|
"horple",
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
char *random_name()
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int mod = 0;
|
||||||
|
for (i = 0; names[i] != NULL; i++) {
|
||||||
|
mod = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return names[random() % mod];
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick(FILE *fp, int init)
|
||||||
|
{
|
||||||
|
struct State *state = malloc(sizeof(struct State));
|
||||||
|
if (!state) {
|
||||||
|
err(EXIT_FAILURE, "Memory error making State");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init == 0) {
|
||||||
|
size_t read = fread(state, sizeof(struct State), 1, fp);
|
||||||
|
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"Read %d \"%s\" (%lu byte(s)) from %lu\n",
|
||||||
|
state->counter, state->name, sizeof(struct State) * read, state->updated_at
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t history[state->history_entries];
|
||||||
|
size_t histories_read = fread(&history, sizeof(time_t), state->history_entries, fp);
|
||||||
|
|
||||||
|
state->counter++;
|
||||||
|
state->updated_at = time(0);
|
||||||
|
strncpy(state->name, random_name(), NAME_SIZE);
|
||||||
|
state->history_entries++;
|
||||||
|
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
|
size_t wrote = fwrite(state, sizeof(struct State), 1, fp);
|
||||||
|
fwrite(history, sizeof(time_t), sizeof(history) / sizeof(time_t), fp);
|
||||||
|
fwrite(&state->updated_at, sizeof(time_t), 1, fp);
|
||||||
|
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"Wrote %d \"%s\" (%lu byte(s)) at %lu (history length=%lu)\n",
|
||||||
|
state->counter, state->name, sizeof(struct State) * wrote, state->updated_at,
|
||||||
|
state->history_entries
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc < 2) {
|
||||||
|
errx(EXIT_FAILURE, "Missing filename argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
int random_seed = time(0);
|
||||||
|
const char *random_seed_str = getenv("RANDOM_SEED");
|
||||||
|
if (random_seed_str) {
|
||||||
|
random_seed = atoi(random_seed_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
srandom(random_seed);
|
||||||
|
|
||||||
|
int init = 0;
|
||||||
|
FILE *fp = fopen(argv[1], "r+");
|
||||||
|
|
||||||
|
if (!fp) {
|
||||||
|
fp = fopen(argv[1], "w+");
|
||||||
|
init = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fp) {
|
||||||
|
err(EXIT_FAILURE, "Failed to open file");
|
||||||
|
}
|
||||||
|
|
||||||
|
tick(fp, init);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
1876
piston-tutorials/getting-started/Cargo.lock
generated
1876
piston-tutorials/getting-started/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1876
piston-tutorials/sudoku/Cargo.lock
generated
1876
piston-tutorials/sudoku/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
|
Binary file not shown.
@@ -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.
|
|
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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);
|
|
||||||
}
|
|
@@ -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);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1 +0,0 @@
|
|||||||
517962483236847915498351762371695248654218397829734156765129834142583679983476520
|
|
@@ -1 +0,0 @@
|
|||||||
016400000200009000400000062070230100100000003003087040960000005000800007000006820
|
|
7
protobuffery/addressbook/go.mod
Normal file
7
protobuffery/addressbook/go.mod
Normal 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
|
6
protobuffery/addressbook/go.sum
Normal file
6
protobuffery/addressbook/go.sum
Normal 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=
|
327
protobuffery/addressbook/pb/addressbook.pb.go
Normal file
327
protobuffery/addressbook/pb/addressbook.pb.go
Normal 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
|
||||||
|
}
|
32
protobuffery/addressbook/pb/addressbook.proto
Normal file
32
protobuffery/addressbook/pb/addressbook.proto
Normal 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;
|
||||||
|
}
|
2
sweeptag/requirements.txt
Normal file
2
sweeptag/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ipython
|
||||||
|
pygame-ce
|
33
sweeptag/sweeptag.py
Normal file
33
sweeptag/sweeptag.py
Normal 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()
|
121
tcp-challenge/server/cmd/server/main.go
Normal file
121
tcp-challenge/server/cmd/server/main.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
tcp-challenge/server/go.mod
Normal file
3
tcp-challenge/server/go.mod
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module git.meatballhat.com/x/box-o-sand/tcp-challenge/server
|
||||||
|
|
||||||
|
go 1.21.4
|
Reference in New Issue
Block a user