chore: восстановление репозитория из снапшота v0.3.1

Прежняя git-история утрачена при переносе проекта на машину владельца
(снапшот без .git). Хэши коммитов в docs/reports/* относятся к утраченной
истории.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
sab.code.lab 2026-08-07 09:34:02 +02:00
commit 1c85091186
184 changed files with 33303 additions and 0 deletions

View file

@ -0,0 +1,345 @@
/**
* Валидация контента этапа 6: задачи (TaskV1 + SGF-деревья решений) и уроки.
* Читает файлы через node:fs, движок только @go-learn/core.
*
* Проверки задач: обязательные поля, уникальность id, префикс id по kind,
* легальность позиции (buildPosition), парсинг SGF, разметка цели на корне
* (TR capture-target/liberties/two-eyes, CR connect) на камнях нужного
* цвета, легальность каждой вариации от позиции задачи (applyMove),
* чередование цветов от toPlay. Сводные: 30 цумэ-го, 3 на каждую
* градацию difficulty 15.
*
* Проверки уроков: frontmatter (title/section/order/summary), section из
* программы курса, существование data-task-id, покрытие всех 10 разделов,
* объём текста 300700 слов.
*/
import { readdirSync, readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import type { BoardSize, BoardState, CellState, Color, GameNode, Point } from '@go-learn/core';
import { applyMove, buildPosition, cellAt, opposite, parseSgf } from '@go-learn/core';
type Goal =
| { readonly type: 'capture-target' }
| { readonly type: 'liberties'; readonly count: number }
| { readonly type: 'connect' }
| { readonly type: 'two-eyes' };
interface TaskV1 {
readonly id: string;
readonly kind: 'lesson' | 'tsumego';
readonly title: string;
readonly topics: readonly string[];
readonly difficulty: number;
readonly size: BoardSize;
readonly toPlay: Color;
readonly setupBlack: readonly Point[];
readonly setupWhite: readonly Point[];
readonly sgf: string;
readonly goal: Goal;
readonly hint: string;
}
const SECTIONS: readonly string[] = [
'Дамэ и захват',
'Атари, лестница, сеть',
'Самоубийство и ко',
'Глаза: жизнь и смерть',
'Подсчёт и конец партии',
'Связь и разрезание',
'Углы, стороны, центр',
'Базовые дзёсэки',
'Форма',
'Простое ёсэ',
];
/** Разделы, в уроках которых обязателен интерактив (уроки 16 и 10). */
const INTERACTIVE_SECTIONS: readonly string[] = [
'Дамэ и захват',
'Атари, лестница, сеть',
'Самоубийство и ко',
'Глаза: жизнь и смерть',
'Подсчёт и конец партии',
'Связь и разрезание',
'Простое ёсэ',
];
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function fail(ctx: string, message: string): never {
throw new Error(`${ctx}: ${message}`);
}
function parsePoints(value: unknown, ctx: string): Point[] {
if (!Array.isArray(value)) fail(ctx, 'ожидался массив точек');
return value.map((item, index) => {
if (!isRecord(item)) fail(`${ctx}[${index}]`, 'точка — не объект');
const { x, y } = item;
if (!Number.isInteger(x) || !Number.isInteger(y)) {
fail(`${ctx}[${index}]`, 'координаты x/y должны быть целыми');
}
return { x: x as number, y: y as number };
});
}
function parseGoal(value: unknown, ctx: string): Goal {
if (!isRecord(value)) fail(ctx, 'goal — не объект');
const type = value['type'];
if (type === 'capture-target' || type === 'connect' || type === 'two-eyes') {
return { type };
}
if (type === 'liberties') {
const count = value['count'];
if (!Number.isInteger(count) || (count as number) < 1) {
fail(ctx, 'goal.count должен быть целым ≥ 1');
}
return { type, count: count as number };
}
return fail(ctx, `неизвестный goal.type «${String(type)}»`);
}
function parseTask(raw: unknown, ctx: string): TaskV1 {
if (!isRecord(raw)) fail(ctx, 'задача — не объект');
const { id, kind, title, topics, difficulty, size, toPlay, sgf, hint } = raw;
if (typeof id !== 'string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(id)) {
fail(ctx, 'id должен быть kebab-case');
}
if (kind !== 'lesson' && kind !== 'tsumego') fail(ctx, 'kind должен быть lesson|tsumego');
const prefix = kind === 'lesson' ? 'lsn-' : 'tsm-';
if (!id.startsWith(prefix)) fail(ctx, `id должен начинаться с «${prefix}»`);
if (typeof title !== 'string' || title.trim() === '') fail(ctx, 'пустой title');
if (
!Array.isArray(topics) ||
topics.length === 0 ||
!topics.every((t) => typeof t === 'string' && t.trim() !== '')
) {
fail(ctx, 'topics должен быть непустым массивом строк');
}
if (!Number.isInteger(difficulty) || (difficulty as number) < 1 || (difficulty as number) > 5) {
fail(ctx, 'difficulty должен быть целым 15');
}
if (size !== 9 && size !== 13 && size !== 19) fail(ctx, 'size должен быть 9|13|19');
if (toPlay !== 'black' && toPlay !== 'white') fail(ctx, 'toPlay должен быть black|white');
if (typeof sgf !== 'string' || sgf.trim() === '') fail(ctx, 'пустой sgf');
if (typeof hint !== 'string' || hint.trim() === '') fail(ctx, 'пустой hint');
const goal = parseGoal(raw['goal'], ctx);
return {
id,
kind,
title,
topics: topics as string[],
difficulty: difficulty as number,
size,
toPlay,
setupBlack: parsePoints(raw['setupBlack'], `${ctx}.setupBlack`),
setupWhite: parsePoints(raw['setupWhite'], `${ctx}.setupWhite`),
sgf,
goal,
hint,
};
}
const here = dirname(fileURLToPath(import.meta.url));
const tasksDir = join(here, 'tasks');
const lessonsDir = join(here, 'lessons');
const loadErrors: string[] = [];
const allTasks: TaskV1[] = [];
const taskFiles = readdirSync(tasksDir)
.filter((name) => name.endsWith('.json'))
.sort();
for (const file of taskFiles) {
try {
const raw: unknown = JSON.parse(readFileSync(join(tasksDir, file), 'utf8'));
if (!Array.isArray(raw)) fail(file, 'корень JSON — не массив');
raw.forEach((item, index) => {
allTasks.push(parseTask(item, `${file}[${index}]`));
});
} catch (error) {
loadErrors.push(error instanceof Error ? error.message : String(error));
}
}
describe('задачи (tasks/*.json)', () => {
it('все файлы парсятся как массивы TaskV1', () => {
expect(loadErrors).toEqual([]);
expect(taskFiles.length).toBeGreaterThan(0);
});
it('id уникальны', () => {
const seen = new Set<string>();
const duplicates: string[] = [];
for (const task of allTasks) {
if (seen.has(task.id)) duplicates.push(task.id);
seen.add(task.id);
}
expect(duplicates).toEqual([]);
});
for (const task of allTasks) {
describe(task.id, () => {
it('позиция легальна (buildPosition не бросает)', () => {
expect(() =>
buildPosition(task.size, task.setupBlack, task.setupWhite, task.toPlay),
).not.toThrow();
});
it('SGF парсится, размер совпадает', () => {
const tree = parseSgf(task.sgf);
expect(tree.size).toBe(task.size);
});
it('на корне есть разметка цели на камнях нужного цвета', () => {
const tree = parseSgf(task.sgf);
const position = buildPosition(task.size, task.setupBlack, task.setupWhite, task.toPlay);
const marked =
task.goal.type === 'connect' ? tree.root.markup.circles : tree.root.markup.triangles;
expect(marked.length).toBeGreaterThan(0);
const want: CellState =
task.goal.type === 'capture-target' ? opposite(task.toPlay) : task.toPlay;
for (const point of marked) {
expect(cellAt(position, point)).toBe(want);
}
});
it('каждая вариация легальна от позиции задачи, цвета чередуются', () => {
const tree = parseSgf(task.sgf);
const initial = buildPosition(task.size, task.setupBlack, task.setupWhite, task.toPlay);
expect(tree.root.children.length).toBeGreaterThan(0);
const walk = (
node: GameNode,
state: BoardState,
expectColor: Color,
path: string,
): void => {
node.children.forEach((child, index) => {
const childPath = `${path}/${index}`;
expect(child.move, `${task.id}${childPath}: узел без хода`).not.toBeNull();
if (child.move === null) return;
expect(child.move.color, `${task.id}${childPath}: нарушено чередование`).toBe(
expectColor,
);
const result = applyMove(state, child.move);
expect(
result.ok,
`${task.id}${childPath}: нелегальный ход ${result.ok === false ? result.error : ''}`,
).toBe(true);
if (result.ok) walk(child, result.state, opposite(expectColor), childPath);
});
};
walk(tree.root, initial, task.toPlay, '');
});
});
}
it('не менее 30 цумэ-го', () => {
expect(allTasks.filter((task) => task.kind === 'tsumego').length).toBeGreaterThanOrEqual(30);
});
it('на каждую градацию difficulty 15 не менее 3 цумэ-го', () => {
for (let difficulty = 1; difficulty <= 5; difficulty += 1) {
const count = allTasks.filter(
(task) => task.kind === 'tsumego' && task.difficulty === difficulty,
).length;
expect(count, `difficulty ${difficulty}`).toBeGreaterThanOrEqual(3);
}
});
it('задачи уроков — на доске 9×9', () => {
for (const task of allTasks.filter((item) => item.kind === 'lesson')) {
expect(task.size, task.id).toBe(9);
}
});
});
interface LessonDoc {
readonly file: string;
readonly frontmatter: Record<string, string> | null;
readonly body: string;
}
function parseFrontmatter(text: string): Record<string, string> | null {
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
if (match === null) return null;
const out: Record<string, string> = {};
for (const line of (match[1] ?? '').split(/\r?\n/)) {
const kv = /^([A-Za-z]+):\s*(.*)$/.exec(line);
if (kv !== null) {
let value = (kv[2] ?? '').trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
out[kv[1] ?? ''] = value;
}
}
return out;
}
const lessonFiles = readdirSync(lessonsDir)
.filter((name) => name.endsWith('.md'))
.sort();
const lessons: LessonDoc[] = lessonFiles.map((file) => {
const text = readFileSync(join(lessonsDir, file), 'utf8');
return { file, frontmatter: parseFrontmatter(text), body: text };
});
describe('уроки (lessons/*.md)', () => {
it('уроков не менее 10', () => {
expect(lessons.length).toBeGreaterThanOrEqual(10);
});
for (const lesson of lessons) {
describe(lesson.file, () => {
it('есть frontmatter с title/section/order/summary', () => {
const fm = lesson.frontmatter;
expect(fm, 'frontmatter не найден').not.toBeNull();
if (fm === null) return;
expect(fm['title'] ?? '', 'title').not.toBe('');
expect(fm['section'] ?? '', 'section').not.toBe('');
expect(fm['summary'] ?? '', 'summary').not.toBe('');
expect(Number.isInteger(Number(fm['order'])), 'order — целое число').toBe(true);
});
it('section входит в программу курса', () => {
expect(SECTIONS).toContain(lesson.frontmatter?.['section']);
});
it('объём текста 300700 слов', () => {
const withoutFrontmatter = lesson.body.replace(/^---\r?\n[\s\S]*?\r?\n---/, '');
const words = withoutFrontmatter.split(/\s+/).filter((word) => /\p{L}/u.test(word));
expect(words.length).toBeGreaterThanOrEqual(300);
expect(words.length).toBeLessThanOrEqual(700);
});
it('все data-task-id существуют среди задач', () => {
const ids = new Set(allTasks.map((task) => task.id));
const refs = [...lesson.body.matchAll(/data-task-id="([^"]+)"/g)].map(
(match) => match[1] ?? '',
);
for (const ref of refs) {
expect(ids.has(ref), `нет задачи «${ref}»`).toBe(true);
}
});
it('разделы 16 и 10 содержат интерактивную задачу', () => {
const section = lesson.frontmatter?.['section'] ?? '';
if (INTERACTIVE_SECTIONS.includes(section)) {
expect(lesson.body).toMatch(/<go-task data-task-id="lsn-/);
}
});
});
}
it('все 10 разделов программы покрыты уроками', () => {
const covered = new Set(lessons.map((lesson) => lesson.frontmatter?.['section'] ?? ''));
for (const section of SECTIONS) {
expect(covered.has(section), `нет урока по разделу «${section}»`).toBe(true);
}
});
});

View file

@ -0,0 +1,74 @@
---
title: 'Дамэ и захват'
section: 'Дамэ и захват'
order: 1
summary: 'Что такое дамэ (дыхания) группы, как считать их и как происходит захват камней. Основа всех правил Го.'
---
# Дамэ и захват
Камень в Го «живёт», пока у него есть **дамэ** — свободные соседние пункты по
линиям доски (по горизонтали и вертикали, диагонали не считаются). Дамэ — это
«дыхание» камня: как только последнее дамэ исчезает, камень снимается с доски.
## Как считать дамэ
У одинокого камня в центре доски четыре дамэ: сверху, снизу, слева и справа.
На краю доски — три, в углу — всего два:
```
A B C A B C
1 . . . 1 X . .
2 . X . 2 . . .
3 . . . 3 . . .
центр: 4 угол A1: 2 (B1 и A2)
```
Камни одного цвета, стоящие рядом по линиям, образуют **группу**. Группа
делит дамэ на всех: у цепочки из двух камней в центре уже шесть дамэ.
Захватывается всегда группа целиком — нельзя снять «половину» цепочки.
## Захват
Когда вы ставите камень так, что у группы противника не остаётся ни одного
дамэ, эта группа немедленно снимается с доски и становится вашими пленниками.
```
A B C
1 . B .
2 B W .
3 . . .
Белый B2 в атari: осталось одно дамэ — B3.
```
Ситуация, когда у группы осталось ровно одно дамэ, называется **атари**
это предупреждение: «ещё один ход, и камни снимут». В обучающих партиях с
новичками «атари» принято говорить вслух — так учатся замечать угрозы;
опытные игроки вслух его обычно не объявляют. Начинающим полезно замечать
атари самостоятельно, пересчитывая дамэ после каждого хода.
## Почему это важно
Всё в Го построено на подсчёте дамэ: и борьба за жизнь группы, и захваты,
и гонки «кто кого первым снимет». Простейшая тактика звучит так: у группы
противника мало дамэ — сокращайте их; у вашей группы мало дамэ — добавляйте
(продлевайтесь, соединяйтесь с друзьями).
Типичная ошибка новичка — не заметить, что его собственная группа уже в
атари, и потерять её. После каждого хода противника спрашивайте себя:
«Сколько теперь дамэ у моих групп?»
## Практика
В задаче ниже белый камень окружён с трёх сторон и у него осталось одно
дамэ. Найдите этот пункт и поставьте туда чёрный камень — белый будет
захвачен.
<go-task data-task-id="lsn-dame-capture">Запасной текст: попробуйте решить задачу (нужен включённый JavaScript).</go-task>
## Запомнить
- Дамэ — свободные соседние пункты камня или группы.
- Нет дамэ — камни снимаются с доски.
- Одно дамэ — атари, группа под угрозой.
- Группа делит дамэ на все свои камни.

View file

@ -0,0 +1,83 @@
---
title: 'Атари, лестница, сеть'
section: 'Атари, лестница, сеть'
order: 1
summary: 'Три базовых тактических приёма: атари как угроза, лестница как погоня по диагонали и сеть как тихий захват без атари.'
---
# Атари, лестница, сеть
## Атари
**Атари** — позиция, в которой у группы осталось одно дамэ. Поставить атари —
значит пригрозить захватом на следующий ход. Атари — самое сильное оружие
новичка: оно часто вынуждает противника отвечать. Впрочем, отвечать не
обязательно: сильный игрок может сознательно проигнорировать атари ради
большего хода в другом месте. Но помните: атари ради атари —
плохая привычка. Каждый раз спрашивайте себя: «А что я получу, если он
спасётся?»
## Лестница
Если загонять камень атари за атари так, что у него всякий раз остаётся
ровно один путь к бегству, получается **лестница** — зигзагообразная погоня
через всю доску:
```
A B C D E
1 . . W B .
2 . B W . .
3 B . . . .
лестница бежит по диагонали к углу
```
Лестница работает, если на её пути нет камней противника. Если по диагонали
бега стоит хоть один вражеский камень (**ситё-прерыватель** — «лестничная
опора», англ. ladder breaker, ход ситё-атари), погоня срывается: убегающая
группа соединяется с опорой и получает новые дамэ. Поэтому перед тем как начать лестницу, прочитайте её до конца:
проследите зигзаг глазами до края доски. Лестница либо захватывает, либо
катастрофически проигрывает — середины нет.
Край доски — лучший друг лестницы: у камня на первой линии всего три дамэ,
и убегать ему некуда. Погоня, прижатая к краю, заканчивается захватом
гарантированно.
## Сеть
Когда лестница не работает, камень часто можно поймать **сетью** (гэта):
тихим ходом, который не ставит атари, но заранее закрывает все пути
к бегству:
```
A B C D
1 . B . .
2 W . . .
3 . . B .
чёрный D-подобный ход накрывает белый камень сетью
```
Классическая сеть ставится через один пункт от цели (конём, как в шахматах).
Куда бы белый ни продлился, следующее атари оставляет его без дамэ.
Признак того, что пора думать о сети: у камня два дамэ, а прямое атари
ведёт в лестницу с вражеской опорой на пути.
## Как выбирать приём
1. Сначала посмотрите на лестницу: чистый ли путь?
2. Если лестница сломана опорой — ищите сеть.
3. Если не работает ни то, ни другое — не гонитесь: слабый камень противника
никуда не денется, займитесь своими группами.
## Практика
В задаче чёрные загоняют белый камень лестницей прямо на край доски.
Проведите всю погоню до захвата.
<go-task data-task-id="lsn-ladder">Запасной текст: попробуйте решить задачу (нужен включённый JavaScript).</go-task>
## Запомнить
- Атари — угроза захвата в один ход.
- Лестница — серия атари, оставляющих один путь; проверяйте диагональ на
наличие опоры.
- Сеть — тихий захват без атари, когда лестница не проходит.

View file

@ -0,0 +1,78 @@
---
title: 'Самоубийство и ко'
section: 'Самоубийство и ко'
order: 1
summary: 'Почему нельзя ставить камень в пункт без дамэ, когда такой ход всё же легален, и как работает правило ко.'
---
# Самоубийство и ко
## Запрет самоубийства
Ход, после которого у вашей собственной группы не остаётся ни одного дамэ,
**запрещён**. Нельзя «пожертвовать» камень, просто поставив его в полностью
окружённый пункт: по официальным правилам (например, AGA) такой ход
отменяется и засчитывается как пас, а на этом сайте движок просто не даст
поставить такой камень.
```
A B C
1 . W .
2 W . W точка B2 для чёрных — под запретом:
3 . W . чёрный камень там сразу останется без дамэ
```
## Важное исключение: захват
Если ход в «запретный» пункт **снимает** группу противника, он легален:
снятые камни освобождают пункты, и у вашего камня появляются дамэ. Поэтому
ход, выглядящий как самоубийство, иногда оказывается решающим ударом.
Всегда проверяйте: не в атари ли группа, внутрь которой вы ставите камень?
## Правило ко
Представьте форму, где чёрные снимают один белый камень, а белые могут
тут же снять в ответ один чёрный — и позиция возвращается к исходной.
Без ограничения такая пара захватов повторялась бы бесконечно. Поэтому
действует правило **ко** (по-японски — «вечность»):
> Немедленно возвращать захваченный камень нельзя. Сначала нужно сделать
> ход в другом месте доски.
```
A B C D
2 . B . .
3 B W . B чёрные снимают W на C3 — белые не могут
4 . W W . ответить захватом сразу: это ко
```
После того как соперник сходил в другую точку, запрет снимается: если он не
закрыл ко, вы можете вернуться и выиграть его.
## Ко-борьба
Из правила ко рождается целая стратегия. Ход в другом месте, который требует
ответа, называется **ко-угрозой**. Выигрывает ко тот, у кого больше угроз:
вы ставите угрозу, соперник отвечает, вы забираете ко обратно. Ценность ко
и ценность угроз нужно сравнивать: иногда выгоднее «продать» ко, получив
два хода подряд в другом месте.
Для начинающего достаточно трёх правил:
1. Начав ко, знайте, зачем: выигрыш ко должен чего-то стоить.
2. Перед ко посчитайте свои ко-угрозы — хотя бы одна должна быть.
3. Не отвечайте на мелкие угрозы, если ко дороже.
## Практика
В задаче чёрные снимают белый камень, образуя ко. Белые не могут вернуть
камень немедленно и ходят в сторону — после этого чёрные спокойно закрывают
ко соединением.
<go-task data-task-id="lsn-ko">Запасной текст: попробуйте решить задачу (нужен включённый JavaScript).</go-task>
## Запомнить
- Ход без дамэ запрещён — если только он не захватывает.
- Ко запрещает немедленный обратный захват одного камня.
- Ко-угроза — способ «купить» право вернуться в ко.

View file

@ -0,0 +1,81 @@
---
title: 'Глаза: жизнь и смерть'
section: 'Глаза: жизнь и смерть'
order: 1
summary: 'Что такое глаз, почему два глаза дают вечную жизнь, и как отличить настоящий глаз от ложного.'
---
# Глаза: жизнь и смерть
## Что такое глаз
**Глаз** — это один или несколько соседних пустых пунктов, полностью
окружённых камнями одного цвета:
```
A B C
1 B B .
2 B . B точка B2 — глаз чёрных
3 B B .
```
Противник не может просто поставить камень в глаз: это самоубийство. Единственный
способ сыграть туда — сначала снять все внешние дамэ группы, то есть поставить
её в атари.
## Два глаза — жизнь
Если у группы есть **два отдельных глаза**, она неуязвима. Чтобы захватить
группу, противнику нужно заполнить оба глаза, но ход в первый глаз —
самоубийство, пока не заполнен второй, и наоборот. Логическая ловушка
замыкается: группа с двумя глазами живёт вечно, сколько бы камней ни
теснилось вокруг.
Отсюда главная цель обороны в Го: **построить два глаза**. И главная цель
нападения: не дать их построить.
Один большой глаз — это ещё не жизнь. Если всё внутреннее пространство группы
связно, противник рано или поздно займёт его ключевую точку. Поэтому
пространство нужно **разделять** на две независимые области.
## Ложный глаз
Не всякий глаз настоящий. Посмотрите на форму:
```
A B C
1 . W .
2 W . W
3 . W .
```
Точка B2 выглядит как глаз белых, но четыре белых камня вокруг неё
**не связаны** между собой — они касаются друг друга только по диагонали.
Чёрные могут поставить атари на один из них, а затем занять «глаз».
Такой глаз называется **ложным**: он есть на бумаге, но не даёт жизни.
Признак настоящего глаза: все камни вокруг него соединены в одну крепкую
группу, и противник не может ни разрезать их, ни поставить в атари.
Прежде чем рассчитывать на глаз, проверьте диагонали: именно по диагоналям
глаз чаще всего оказывается ложным.
## Жизнь и смерть в задачах
Типичная задача на жизнь и смерть формулируется так: «чёрные ходят и живут»
или «чёрные ходят и убивают». Решение почти всегда — **жизненная точка**:
единственный пункт, который делит пространство на два глаза (для жизни) или
мешает противнику это сделать (для убийства). Ищите центр симметрии формы —
чаще всего он и есть ответ.
## Практика
Белые рассчитывают на глаз, но их камни вокруг него не связаны — глаз ложный.
Найдите ход, который разрушает иллюзию и захватывает отмеченные камни.
<go-task data-task-id="lsn-false-eye">Запасной текст: попробуйте решить задачу (нужен включённый JavaScript).</go-task>
## Запомнить
- Глаз — пустое пространство, окружённое одним цветом.
- Два глаза = вечная жизнь; один глаз = смерть под вопросом.
- Ложный глаз не защищает: проверяйте связность камней вокруг него.

View file

@ -0,0 +1,76 @@
---
title: 'Подсчёт и конец партии'
section: 'Подсчёт и конец партии'
order: 1
summary: 'Как заканчивается партия (два паса), что такое территория и как подсчитывается результат по китайским правилам.'
---
# Подсчёт и конец партии
## Когда партия заканчивается
Партия в Го не заканчивается матом — она заканчивается согласием. Когда оба
игрока считают, что выгодных ходов не осталось, они пасуют. **Два паса
подряд — конец партии.**
Перед пасом проверьте:
- все ли границы территорий закрыты;
- нет ли незакрытых дамэ между группами, через которые противник может
разрезать;
- не осталось ли ваших камней в атари, которые ещё можно спасти, и чужих —
которые можно добрать.
Пасовать раньше времени — отдавать очки. Пасовать позже — тоже ошибка:
по японским правилам ход в свою территорию стоит очко, а по китайским
(нашим) счёт не меняется, но ход впустую отдаёт инициативу.
## Мёртвые камни
После двух пасов игроки договариваются, какие группы **мёртвы** — обречены,
хотя формально стоят на доске. Мёртвые камни убираются с доски как пленники.
Если игроки не согласны — партия просто продолжается, и спорная группа
доказывает свою судьбу игрой. Это честное правило: «сомневаешься — играй».
## Что такое территория
**Территория** — пустые пункты, окружённые живыми камнями одного цвета так,
что противник не может вторгнуться с надеждой выжить. Каждый такой пункт —
очко.
## Подсчёт по-китайски (площадь)
Мы используем китайские правила: очки каждого игрока — это
> **его живые камни на доске + его территория.**
Белые дополнительно получают **коми** — компенсацию за второй ход
(в наших партиях — 6,5 очка на большой доске и 5,5 — на доске 9×9; стандарт
китайских правил на большой доске — 7,5; пол-очка исключает ничью).
Пример. У чёрных: 30 камней на доске и 20 пунктов территории — 50 очков.
У белых: 28 камней и 16 пунктов территории — 44, плюс коми 5,5 — итого 49,5.
Чёрные выиграли с перевесом 0,5 очка.
Обратите внимание: по китайским правилам ход внутрь своей территории не
меняет счёта (камень занимает пункт территории), а по японским — потерял бы
очко. Поэтому в конце партии по нашим правилам можно безопасно «доукрепляться».
## Оценка позиции по ходу партии
Грубая прикидка «кто выигрывает» — полезный навык: посчитайте очевидную
территорию обоих, добавьте коми белым, сравните. Если вы впереди — играйте
проще и надёжнее; если позади — ищите более острые варианты.
## Практика
Перед подсчётом нужно прибрать доску: два белых камня внутри чёрной
территории в атари. Снимите их — иначе при подсчёте начнутся споры.
<go-task data-task-id="lsn-endgame-capture">Запасной текст: попробуйте решить задачу (нужен включённый JavaScript).</go-task>
## Запомнить
- Два паса подряд заканчивают партию.
- Счёт: камни + территория; белые получают коми.
- Мёртвые камни снимаются по согласию; спор решается доигрыванием.

View file

@ -0,0 +1,75 @@
---
title: 'Связь и разрезание'
section: 'Связь и разрезание'
order: 1
summary: 'Почему связанные группы сильнее, как соединять свои камни надёжно и как разрезать камни противника.'
---
# Связь и разрезание
## Сила связи
Камни, соединённые в одну группу, делят дамэ и строят глаза вместе.
Две слабые группы по три дамэ — две проблемы; одна связанная группа с шестью
дамэ — крепость. Поэтому **соединяйте свои группы и разрезайте чужие**
это называют главной тактической идеей Го.
## Надёжные и ненадёжные связи
Камни, стоящие вплотную по линии, связаны абсолютно — их нельзя разрезать.
Но многие «красивые» формы связаны лишь условно:
```
A B C D
1 B . B . ход через один пункт (иккэн):
2 . . . . белые могут попытаться вклиниться в B1
```
Связь через один пункт надёжна, только если вы готовы ответить соединением
на вклинивание. Диагональный ход (косуми) — тоже не абсолютная связь, но
сам по себе он локально неразрезаем: на разрез в один пункт вы отвечаете
соединением в другой (миаи). Уязвимо косуми лишь тогда, когда вокруг уже
стоят сильные камни противника.
## Типичные способы соединения
- **Прямое соединение** — поставить камень в разрыв цепочки.
- **Соединение снизу** — соединиться по первой линии, под защитой края.
- **Захват разрезающего камня** — если вклинившийся камень слаб, снимите его:
связь восстановится сама собой.
Выбирая, соединяться ли, спросите: «Что случится, если меня разрежут?»
Если обе части живут сами — не тратьте ход. Если одна погибнет — соединяйтесь
немедленно.
## Как разрезать
Разрезание — зеркальный приём: ищите **точку разреза** — пункт, через который
противник соединял бы свои группы. Часто это единственный пункт между двумя
диагонально стоящими камнями. Но помните: разрезающий ход должен сам выжить.
Разрезать и тут же потерять разрезающий камень — значит подарить противнику
и связь, и материал.
## Когда связь важнее территории
Новички жертвуют связью ради пары пунктов. Почти всегда это ошибка:
разрезанная группа бежит, отдаёт инициативу и тащит за собой потери по всей
доске. Сильные игроки говорят: «Соединяйся — и получишь ход в другом месте».
Полезно и обратное наблюдение: если противник потратил ход на разрезание,
а обе ваши половины спокойно живут, — он просто подарил вам темп. Ценность
разреза и связи всегда измеряется судьбой групп, а не самим фактом
«разрезал» или «соединил».
## Практика
Белый камень вклинился между двумя чёрными группами, но он в атари. Снимите
его — и группы соединятся сами.
<go-task data-task-id="lsn-connect">Запасной текст: попробуйте решить задачу (нужен включённый JavaScript).</go-task>
## Запомнить
- Связанная группа сильнее суммы частей.
- Косуми само по себе неразрезаемо, но уязвимо при сильных камнях вокруг.
- Разрезая, позаботьтесь о жизни разрезающего камня.

View file

@ -0,0 +1,70 @@
---
title: 'Углы, стороны, центр'
section: 'Углы, стороны, центр'
order: 1
summary: 'Почему партия начинается с углов, как край доски работает готовой стеной, и что такое линии доски.'
---
# Углы, стороны, центр
## Неравенство частей доски
Территория в Го строится у краёв доски, потому что сам край работает как
готовая стена. Сравните, сколько стен нужно построить, чтобы оградить
территорию:
- **Угол**: два края доски — уже две готовые стены, достроить остаётся две.
- **Сторона**: один край — одна готовая стена, строить придётся три.
- **Центр**: краёв нет — нужны все четыре стены.
Отсюда знаменитый порядок начала партии: **сначала углы, потом стороны,
потом центр**. Новичок, тянущийся в центр с первых ходов, строит забор
посреди поля — дорого и бесполезно.
## Линии доски
У линий доски есть имена и характер:
- **Первая линия** — край доски: у камня на ней всего три дамэ, в дебюте
туда играть не стоит.
- **Вторая линия** — «линия поражения»: территория есть, но мала и уязвима.
- **Третья линия** — «линия территории»: оптимум для ограждения земли.
- **Четвёртая линия** — «линия влияния»: территории меньше, зато больше
давления на центр и стороны.
Классический баланс дебюта — игра на третьей и четвёртой линиях.
## Ключевые пункты угла
- **3-3** (сан-сан): камень глубоко в углу. Надёжно забирает угол, но низко,
мало влияния.
- **3-4** (комоку): самый популярный первый ход. Угол почти ваш, остаётся
гибкость.
- **4-4** (хоси): звёздный пункт. Угол можно оспорить (вторжение 3-3), зато
камень сильнее тянется к центру.
- **3-5** (мокухадзуси) и **4-5** (такамоку) — реже, с уклоном в сторону
и влияние соответственно.
## Что делает центр полезным
Центр не даёт много территории напрямую, но камни в центре:
- перерезают пути бегства и соединения;
- усиливают вторжения и погони (лестницы!);
- превращают соседние стороны в большие рамки (**мойо**).
Центр — это сила, а не земля. Играть туда стоит, когда углы и стороны уже
заняты и началась борьба.
## Практический рецепт для новичка
1. Первые ходы — в свободные углы (3-4 или 4-4).
2. Дальше — подходы к углам и задержки по сторонам.
3. В центр — только с конкретной целью: атака, спасение, перерезание.
4. Никогда не играйте на первой-второй линии в дебюте без причины.
## Запомнить
- Край доски — бесплатная стена; центр её лишён.
- Порядок дебюта: углы → стороны → центр.
- Третья линия — территория, четвёртая — влияние.

View file

@ -0,0 +1,82 @@
---
title: 'Базовые дзёсэки'
section: 'Базовые дзёсэки'
order: 1
summary: 'Что такое дзёсэки, зачем их учить, и три простейших угловых образца: занятие угла 3-3, цукэ-хики и вторжение 3-3.'
---
# Базовые дзёсэки
## Что такое дзёсэки
**Дзёсэки** — устоявшаяся «правильная» последовательность ходов в углу,
проверенная поколениями игроков. Итог дзёсэки считается равным (или
почти равным) для обеих сторон: один получает угол, другой — стену или
инициативу.
Зачем учить дзёсэки новичку? Не для зазубривания, а чтобы:
- не проигрывать угол за пять ходов из-за типовой ошибки;
- понимать типичные формы: где слабость, где разрез, где жизненная точка;
- освободить внимание для борьбы в других местах доски.
Важно: дзёсэки — не догма. «Правильный» ход зависит от всей доски; на 9×9
влияние центра сильнее, чем на 19×19, и многие классические дзёсэки там
ведут себя иначе. Учите идею, а не только порядок ходов.
## Дзёсэки 1: занятие угла 3-3
Простейший способ забрать угол — поставить камень прямо на пункт 3-3.
Соперник прижмёт сверху, вы ответите по краю:
```
A B C D E
1 . . . . .
2 . . W . .
3 . . B . .
4 . . . . .
```
Чёрный камень на C3 почти неуязвим: у него сразу задел на угол.
Дальше белые обычно давят снаружи, а чёрные ползут по второй-третьей линии,
выстраивая жизнь. Идея: **угол — мой, влияние — ваше**.
## Дзёсэки 2: цукэ-хики — прижатие и отступление
Чёрные заняли угол ходом 3-4, белые подошли к нему. Классический образец —
**цукэ-хики**: чёрные прижимаются к подошедшему камню (**цукэ**), белые
отвечают **ханэ** (изгибом вокруг чёрного камня), а чёрные спокойно
отступают (**хики**):
```
A B C D E
1 . . . . .
2 . . B . .
3 . B W . .
4 . . . . .
```
Хики укрепляет угол чёрных, белые получают внешнюю позицию — это один из
первых дзёсэки, которые стоит узнать новичку. Ключевой урок:
**отступление — не трусость**, а способ сохранить форму без разрезов.
## Дзёсэки 3: вторжение 3-3 под хоси
Если соперник начал с 4-4 (хоси), угол ещё не закрыт — вторгайтесь на 3-3.
Дальше развивается типовая борьба: хозяин хоси давит с двух сторон,
вторгшийся ползёт по краю и живёт маленькой группой. Хозяин строит внешнюю
стену. Итог: земля против влияния, равенство.
## Как относиться к дзёсэки на практике
1. Знайте два-три образца для каждого углового пункта — этого хватит надолго.
2. После каждой партии смотрите: не вышел ли угол «криво»? Если да —
найдите, где отступили от образца.
3. Помните, что дзёсэки обновляются: профессионалы постоянно находят новые
варианты, а с появлением сильных программ многие оценки изменились.
## Запомнить
- Дзёсэки — эталонные угловые размены, равные для обеих сторон.
- Учите смысл ходов, а не только их порядок.
- На малой доске классические дзёсэки работают с поправкой на центр.

View file

@ -0,0 +1,84 @@
---
title: 'Форма'
section: 'Форма'
order: 1
summary: 'Хорошие и плохие формы камней: тигровая пасть, бамбук, пустой треугольник, данго. Как форма влияет на эффективность.'
---
# Форма
## Что такое форма
Два игрока могут поставить одинаковое число камней, но у одного они будут
работать на все сто процентов, а у другого — вполсилы. Разница — в **форме**:
в том, насколько камни связаны, гибки и лишены слабостей. Хорошая форма
экономит ходы; плохая форма тащит лишний вес.
## Хорошие формы
**Тигровая пасть (кошачья пасть)** — три камня вокруг пустого пункта:
```
A B C
1 . B .
2 B . .
3 . B .
```
Пустой пункт — почти соединение: если противник вклинится туда, вы ответите
и соединитесь окончательно. Пасть защищает разрез одним камнем.
**Бамбук (такэфу)** — два камня вплотную и ещё два через пункт:
```
A B C D E
1 B B . B B
2 . . . . .
```
Связь короткая, пружинящая: разрезать бамбук почти невозможно, а тянется он
быстро.
**Вытягивание (ноби)** — простой ход вперёд от своего камня. Скромный, но
самый надёжный способ добавить дамэ и не создать слабостей.
## Плохие формы
**Пустой треугольник** — три своих камня треугольником вокруг пустого пункта,
который ничего не защищает:
```
A B C
1 . B .
2 B B .
```
Три камня делают работу двух: тот же пункт можно было прикрыть экономнее.
Поговорка гласит: «пустой треугольник — не ставь никогда».
**Данго** — «пельменная форма»: бесформенный ком камней без глаз и с малым
числом дамэ. Такая масса тяжела: её легко атаковать, а камни в ней работают
плохо. Данго — признак того, что вас гонят, а вы просто соединяетесь, не
думая.
**Перегруппировка** — слишком много камней в малой области: они мешают друг
другу строить глаза и дают противнику лишние точки атаки.
## Как вырабатывать вкус формы
1. После каждого хода спрашивайте: «Не пустой ли это треугольник?»
2. Предпочитайте ходы, которые делают две вещи сразу: соединяют и строят
глаз, атакуют и укрепляются.
3. Смотрите на свои группы целиком: есть ли у них «головы» (выход наружу),
«ноги» (опора у края) и место для глаз?
4. Пересматривайте свои партии и отмечайте тяжёлые формы — со временем они
начнут сами резать глаз.
Форма — это эстетика, проверенная логикой: красивый ход в Го почти всегда
оказывается и сильным.
## Запомнить
- Тигровая пасть и бамбук — образцы экономной защиты.
- Пустой треугольник и данго — типичные ошибки новичка.
- Хорошая форма = связность + гибкость + потенциал глаз.

View file

@ -0,0 +1,73 @@
---
title: 'Простое ёсэ'
section: 'Простое ёсэ'
order: 1
summary: 'Эндшпиль по-гошному: ханэ на первой линии, сэнтэ и готэ, почему мелкие ходы в конце решают партию.'
---
# Простое ёсэ
## Что такое ёсэ
**Ёсэ** — эндшпиль: стадия, когда большие территории уже очерчены и игроки
уточняют границы. Ходы ёсэ обычно мельче ходов миттельшпиля, но среди них
бывают и дорогие: комбинация ханэ у края может стоить около десяти очков.
Партия между равными часто решается именно здесь. Пропустить пару ходов
ёсэ — значит проиграть выигранную партию.
## Ханэ на первой линии
Классический приём ёсэ — **ханэ у края**: ход, изгибающийся вокруг камня
противника на первой линии:
```
A B C D
8 B W . .
9 B W . .
```
Ханэ отодвигает границу на один пункт в вашу пользу. Если соперник ответит —
вы сохранили инициативу; если нет — вы продолжаете ползти дальше. Пара ханэ
«с двух сторон» одной и той же границы стоит уже несколько очков.
## Сэнтэ и готэ
- **Сэнтэ** — ход, требующий ответа: после него инициатива (право ходить
первым в новом месте) остаётся у вас.
- **Готэ** — ход, на который можно не отвечать: инициатива переходит
сопернику.
Правило ёсэ: **сначала играйте сэнтэ-ходы, потом готэ.** Сэнтэ бесплатен:
соперник ответит, и вы снова выбираете, куда ходить. Готэ-ход «покупает»
пункты ценой инициативы — берите его, когда сэнтэ кончились.
## Как оценивать ходы ёсэ
Считайте разницу «мы сыграли здесь / соперник сыграл здесь». Если ваш ход
даёт +2 очка вам, а ход соперника там же дал бы +2 ему, ценность пункта —
4 очка. Берите самые дорогие пункты первыми. Когда останутся только
одноочковые размены (**дамэ** — нейтральные пункты), партия близка к концу.
## Типичные ошибки новичка в ёсэ
1. Играть в свою территорию «для надёжности» — ход ничего не защищает,
а инициативу отдаёт.
2. Отвечать на готэ-угрозы — соперник даром получает темп.
3. Не добрать последние атари: мёртвые на вид камни иногда спасаются
на одну темповую ошибку.
Хорошая привычка на 9×9 — за десяток ходов до конца мысленно прикинуть
счёт: это подскажет, нужен ли спокойный ёсэ или пора рисковать.
## Практика
Чёрные прижимают белых к самому краю: краевая погоня в духе ёсэ заканчивается
захватом двух камней. Найдите последовательность до конца.
<go-task data-task-id="lsn-yose">Запасной текст: попробуйте решить задачу (нужен включённый JavaScript).</go-task>
## Запомнить
- Ёсэ — игра за очки на границах; мелочей не бывает.
- Сэнтэ раньше готэ; считайте ценность каждого пункта.
- Ханэ на первой линии — рабочая лошадка эндшпиля.

View file

@ -0,0 +1,284 @@
[
{
"id": "lsn-dame-capture",
"kind": "lesson",
"title": "Последнее дамэ",
"topics": ["атари", "захват"],
"difficulty": 1,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 3,
"y": 4
},
{
"x": 5,
"y": 4
},
{
"x": 4,
"y": 3
}
],
"setupWhite": [
{
"x": 4,
"y": 4
}
],
"sgf": "(;GM[1]SZ[9]TR[ee];B[ef])",
"goal": {
"type": "capture-target"
},
"hint": "У белого камня осталось одно дамэ — займите его."
},
{
"id": "lsn-ladder",
"kind": "lesson",
"title": "Лестница у края",
"topics": ["лестница", "атари"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 1,
"y": 1
},
{
"x": 2,
"y": 2
}
],
"setupWhite": [
{
"x": 2,
"y": 1
}
],
"sgf": "(;GM[1]SZ[9]TR[cb];B[db];W[ca];B[ba];W[da];B[ea])",
"goal": {
"type": "capture-target"
},
"hint": "Гоните камень к краю: каждое атари оставляет белым один путь."
},
{
"id": "lsn-ko",
"kind": "lesson",
"title": "Захват в ко",
"topics": ["захват"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 3,
"y": 4
},
{
"x": 4,
"y": 3
},
{
"x": 5,
"y": 4
}
],
"setupWhite": [
{
"x": 4,
"y": 4
},
{
"x": 3,
"y": 5
},
{
"x": 5,
"y": 5
},
{
"x": 4,
"y": 6
}
],
"sgf": "(;GM[1]SZ[9]TR[ee];B[ef];W[aa];B[ee])",
"goal": {
"type": "capture-target"
},
"hint": "Снимите камень — белые не смогут вернуть его сразу: это ко."
},
{
"id": "lsn-false-eye",
"kind": "lesson",
"title": "Ложный глаз",
"topics": ["глаза", "захват"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 0,
"y": 2
},
{
"x": 1,
"y": 3
},
{
"x": 2,
"y": 2
},
{
"x": 3,
"y": 1
},
{
"x": 2,
"y": 0
}
],
"setupWhite": [
{
"x": 0,
"y": 1
},
{
"x": 1,
"y": 0
},
{
"x": 2,
"y": 1
},
{
"x": 1,
"y": 2
}
],
"sgf": "(;GM[1]SZ[9]TR[bc][cb];B[bb];W[ii])",
"goal": {
"type": "capture-target"
},
"hint": "Камни вокруг «глаза» не связаны — займите центр и снимите два камня."
},
{
"id": "lsn-endgame-capture",
"kind": "lesson",
"title": "Чистка территории",
"topics": ["захват", "ёсэ"],
"difficulty": 1,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 3,
"y": 2
},
{
"x": 4,
"y": 2
},
{
"x": 2,
"y": 3
},
{
"x": 5,
"y": 3
},
{
"x": 3,
"y": 4
}
],
"setupWhite": [
{
"x": 3,
"y": 3
},
{
"x": 4,
"y": 3
}
],
"sgf": "(;GM[1]SZ[9]TR[dd][ed];B[ee])",
"goal": {
"type": "capture-target"
},
"hint": "Два белых камня в атари — доберите их перед подсчётом."
},
{
"id": "lsn-connect",
"kind": "lesson",
"title": "Соединение через разрез",
"topics": ["связь", "разрезание"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 2,
"y": 2
},
{
"x": 3,
"y": 2
},
{
"x": 2,
"y": 4
},
{
"x": 3,
"y": 4
},
{
"x": 4,
"y": 3
}
],
"setupWhite": [
{
"x": 3,
"y": 3
}
],
"sgf": "(;GM[1]SZ[9]CR[dc][de];B[cd];W[bd])",
"goal": {
"type": "connect"
},
"hint": "Снимите разрезающий камень — группы соединятся сами."
},
{
"id": "lsn-yose",
"kind": "lesson",
"title": "Ёсэ у края",
"topics": ["ёсэ", "захват"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 1,
"y": 7
}
],
"setupWhite": [
{
"x": 0,
"y": 7
},
{
"x": 0,
"y": 8
}
],
"sgf": "(;GM[1]SZ[9]TR[ah][ai];B[ag];W[bi];B[ci])",
"goal": {
"type": "capture-target"
},
"hint": "Прижмите белых к краю: они не убегут."
}
]

View file

@ -0,0 +1,751 @@
[
{
"id": "tsm-atari-corner",
"kind": "tsumego",
"title": "Атари в углу",
"topics": ["атари", "захват"],
"difficulty": 1,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 1,
"y": 0
}
],
"setupWhite": [
{
"x": 0,
"y": 0
}
],
"sgf": "(;GM[1]SZ[9]TR[aa];B[ab])",
"goal": {
"type": "capture-target"
},
"hint": "У камня в углу всего два дамэ, одно уже занято."
},
{
"id": "tsm-atari-edge",
"kind": "tsumego",
"title": "Атари на краю",
"topics": ["атари"],
"difficulty": 1,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 0,
"y": 3
},
{
"x": 1,
"y": 4
}
],
"setupWhite": [
{
"x": 0,
"y": 4
}
],
"sgf": "(;GM[1]SZ[9]TR[ae];B[af])",
"goal": {
"type": "capture-target"
},
"hint": "Осталось одно дамэ."
},
{
"id": "tsm-atari-center",
"kind": "tsumego",
"title": "Одинокий камень",
"topics": ["атари", "захват"],
"difficulty": 1,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 3,
"y": 4
},
{
"x": 5,
"y": 4
},
{
"x": 4,
"y": 3
}
],
"setupWhite": [
{
"x": 4,
"y": 4
}
],
"sgf": "(;GM[1]SZ[9]TR[ee];B[ef])",
"goal": {
"type": "capture-target"
},
"hint": "Закройте последнее дамэ."
},
{
"id": "tsm-capture-pair",
"kind": "tsumego",
"title": "Два камня в сети",
"topics": ["захват"],
"difficulty": 1,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 3,
"y": 4
},
{
"x": 5,
"y": 4
},
{
"x": 4,
"y": 3
},
{
"x": 3,
"y": 5
},
{
"x": 5,
"y": 5
}
],
"setupWhite": [
{
"x": 4,
"y": 4
},
{
"x": 4,
"y": 5
}
],
"sgf": "(;GM[1]SZ[9]TR[ee][ef];B[eg])",
"goal": {
"type": "capture-target"
},
"hint": "У группы из двух камней одно общее дамэ."
},
{
"id": "tsm-capture-edge-pair",
"kind": "tsumego",
"title": "Пара у края",
"topics": ["захват", "атари"],
"difficulty": 1,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 1,
"y": 3
},
{
"x": 1,
"y": 4
},
{
"x": 0,
"y": 2
}
],
"setupWhite": [
{
"x": 0,
"y": 3
},
{
"x": 0,
"y": 4
}
],
"sgf": "(;GM[1]SZ[9]TR[ad][ae];B[af])",
"goal": {
"type": "capture-target"
},
"hint": "Край доски не даёт лишних дамэ."
},
{
"id": "tsm-ladder-edge",
"kind": "tsumego",
"title": "Лестница к углу",
"topics": ["лестница"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 5,
"y": 1
},
{
"x": 6,
"y": 2
}
],
"setupWhite": [
{
"x": 6,
"y": 1
}
],
"sgf": "(;GM[1]SZ[9]TR[gb];B[hb];W[ga];B[fa];W[ha];B[ia])",
"goal": {
"type": "capture-target"
},
"hint": "Атари за атари — белые упираются в угол."
},
{
"id": "tsm-double-atari",
"kind": "tsumego",
"title": "Двойное атари",
"topics": ["атари", "захват"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 4,
"y": 2
},
{
"x": 3,
"y": 1
},
{
"x": 4,
"y": 4
},
{
"x": 3,
"y": 5
}
],
"setupWhite": [
{
"x": 3,
"y": 2
},
{
"x": 3,
"y": 4
}
],
"sgf": "(;GM[1]SZ[9]TR[dc];B[dd];W[ce];B[cc])",
"goal": {
"type": "capture-target"
},
"hint": "Один ход — два атари; белые спасут лишь один камень."
},
{
"id": "tsm-liberties-escape",
"kind": "tsumego",
"title": "Выход из атари",
"topics": ["атари"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 4,
"y": 4
}
],
"setupWhite": [
{
"x": 3,
"y": 4
},
{
"x": 4,
"y": 3
},
{
"x": 4,
"y": 5
}
],
"sgf": "(;GM[1]SZ[9]TR[ee];B[fe];W[ge])",
"goal": {
"type": "liberties",
"count": 3
},
"hint": "Вытяните камень — пусть у группы станет три дамэ."
},
{
"id": "tsm-kill-corner",
"kind": "tsumego",
"title": "Угол без глаз",
"topics": ["захват", "жизнь и смерть"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 2,
"y": 0
}
],
"setupWhite": [
{
"x": 0,
"y": 0
},
{
"x": 1,
"y": 0
}
],
"sgf": "(;GM[1]SZ[9]TR[aa][ba];B[bb];W[ab];B[ac])",
"goal": {
"type": "capture-target"
},
"hint": "Займите точку глаза — белым некуда бежать."
},
{
"id": "tsm-ladder-down",
"kind": "tsumego",
"title": "Лестница вниз",
"topics": ["лестница"],
"difficulty": 3,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 1,
"y": 7
},
{
"x": 2,
"y": 6
}
],
"setupWhite": [
{
"x": 2,
"y": 7
}
],
"sgf": "(;GM[1]SZ[9]TR[ch];B[dh];W[ci];B[bi];W[di];B[ei])",
"goal": {
"type": "capture-target"
},
"hint": "Та же лестница, но к нижнему краю."
},
{
"id": "tsm-capture-three-corner",
"kind": "tsumego",
"title": "Тройка в углу",
"topics": ["захват", "жизнь и смерть"],
"difficulty": 3,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 2,
"y": 0
},
{
"x": 2,
"y": 1
},
{
"x": 0,
"y": 2
}
],
"setupWhite": [
{
"x": 0,
"y": 0
},
{
"x": 1,
"y": 0
},
{
"x": 1,
"y": 1
}
],
"sgf": "(;GM[1]SZ[9]TR[aa][ba][bb];B[bc];W[ii];B[ab])",
"goal": {
"type": "capture-target"
},
"hint": "Белые не могут выйти: любое продление — самоубийство."
},
{
"id": "tsm-liberties-wall",
"kind": "tsumego",
"title": "Прорвите стену",
"topics": ["атари", "жизнь и смерть"],
"difficulty": 3,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 4,
"y": 2
},
{
"x": 4,
"y": 3
}
],
"setupWhite": [
{
"x": 3,
"y": 2
},
{
"x": 5,
"y": 2
},
{
"x": 3,
"y": 3
},
{
"x": 5,
"y": 3
}
],
"sgf": "(;GM[1]SZ[9]TR[ec][ed];B[ee];W[de])",
"goal": {
"type": "liberties",
"count": 4
},
"hint": "Вытяните группу вниз — нужно четыре дамэ."
},
{
"id": "tsm-ko-fight",
"kind": "tsumego",
"title": "Выигранное ко",
"topics": ["захват"],
"difficulty": 4,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 6,
"y": 3
},
{
"x": 5,
"y": 4
},
{
"x": 7,
"y": 4
}
],
"setupWhite": [
{
"x": 6,
"y": 4
},
{
"x": 5,
"y": 5
},
{
"x": 7,
"y": 5
},
{
"x": 6,
"y": 6
}
],
"sgf": "(;GM[1]SZ[9]TR[ge];B[gf];W[aa];B[ge])",
"goal": {
"type": "capture-target"
},
"hint": "Начните ко — у белых нет угроз, закройте его."
},
{
"id": "tsm-double-atari-save",
"kind": "tsumego",
"title": "Двойная угроза",
"topics": ["атари", "захват"],
"difficulty": 4,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 2,
"y": 3
},
{
"x": 4,
"y": 3
},
{
"x": 3,
"y": 2
},
{
"x": 2,
"y": 5
},
{
"x": 4,
"y": 5
}
],
"setupWhite": [
{
"x": 3,
"y": 3
},
{
"x": 3,
"y": 5
}
],
"sgf": "(;GM[1]SZ[9]TR[dd];B[de](;W[dg])(;W[ii]))",
"goal": {
"type": "capture-target"
},
"hint": "Захват и атари одним ходом — белые не спасут оба камня."
},
{
"id": "tsm-corner-chase",
"kind": "tsumego",
"title": "Погоня по краю",
"topics": ["захват", "жизнь и смерть"],
"difficulty": 4,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 1,
"y": 4
},
{
"x": 2,
"y": 5
},
{
"x": 0,
"y": 3
},
{
"x": 1,
"y": 3
}
],
"setupWhite": [
{
"x": 0,
"y": 4
},
{
"x": 0,
"y": 5
},
{
"x": 1,
"y": 5
}
],
"sgf": "(;GM[1]SZ[9]TR[ae][af][bf];B[bg];W[ag];B[ah])",
"goal": {
"type": "capture-target"
},
"hint": "Сокращайте дамэ по очереди — край не оставит места."
},
{
"id": "tsm-capture-race",
"kind": "tsumego",
"title": "Гонка захватов",
"topics": ["захват", "жизнь и смерть"],
"difficulty": 4,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 6,
"y": 8
},
{
"x": 7,
"y": 8
},
{
"x": 7,
"y": 6
}
],
"setupWhite": [
{
"x": 8,
"y": 8
},
{
"x": 8,
"y": 7
}
],
"sgf": "(;GM[1]SZ[9]TR[ih][ii];B[hh];W[ig];B[if])",
"goal": {
"type": "capture-target"
},
"hint": "У белых два дамэ, у вас темп — начинайте первым."
},
{
"id": "tsm-ko-threat",
"kind": "tsumego",
"title": "Ко с угрозой",
"topics": ["захват"],
"difficulty": 5,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 2,
"y": 5
},
{
"x": 3,
"y": 4
},
{
"x": 4,
"y": 5
}
],
"setupWhite": [
{
"x": 3,
"y": 5
},
{
"x": 2,
"y": 6
},
{
"x": 4,
"y": 6
},
{
"x": 3,
"y": 7
}
],
"sgf": "(;GM[1]SZ[9]TR[df];B[dg];W[ii];B[df])",
"goal": {
"type": "capture-target"
},
"hint": "Захватите ко, переждите угрозу и закройте его."
},
{
"id": "tsm-chase-six",
"kind": "tsumego",
"title": "Шесть камней к краю",
"topics": ["захват", "жизнь и смерть"],
"difficulty": 5,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 1,
"y": 1
},
{
"x": 1,
"y": 2
},
{
"x": 1,
"y": 3
},
{
"x": 1,
"y": 4
},
{
"x": 1,
"y": 5
}
],
"setupWhite": [
{
"x": 0,
"y": 1
},
{
"x": 0,
"y": 2
},
{
"x": 0,
"y": 3
},
{
"x": 0,
"y": 4
},
{
"x": 0,
"y": 5
}
],
"sgf": "(;GM[1]SZ[9]TR[ab][ac][ad][ae][af];B[ag];W[aa];B[ba])",
"goal": {
"type": "capture-target"
},
"hint": "Выберите правильный конец — второй ведёт в угол."
},
{
"id": "tsm-liberties-five",
"kind": "tsumego",
"title": "Пять дамэ",
"topics": ["атари", "жизнь и смерть"],
"difficulty": 5,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 4,
"y": 4
},
{
"x": 4,
"y": 5
}
],
"setupWhite": [
{
"x": 3,
"y": 4
},
{
"x": 5,
"y": 4
},
{
"x": 3,
"y": 5
},
{
"x": 5,
"y": 5
}
],
"sgf": "(;GM[1]SZ[9]TR[ee][ef];B[ed];W[eg];B[ec])",
"goal": {
"type": "liberties",
"count": 5
},
"hint": "Одного продления мало — вытягивайтесь дважды."
}
]

View file

@ -0,0 +1,432 @@
[
{
"id": "tsm-eyes-three",
"kind": "tsumego",
"title": "Три в ряд",
"topics": ["глаза", "жизнь и смерть"],
"difficulty": 1,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 0,
"y": 0
},
{
"x": 1,
"y": 0
},
{
"x": 2,
"y": 0
},
{
"x": 0,
"y": 1
},
{
"x": 2,
"y": 1
},
{
"x": 0,
"y": 2
},
{
"x": 2,
"y": 2
},
{
"x": 0,
"y": 3
},
{
"x": 2,
"y": 3
},
{
"x": 0,
"y": 4
},
{
"x": 1,
"y": 4
},
{
"x": 2,
"y": 4
}
],
"setupWhite": [
{
"x": 3,
"y": 0
},
{
"x": 3,
"y": 1
},
{
"x": 3,
"y": 2
},
{
"x": 3,
"y": 3
},
{
"x": 3,
"y": 4
},
{
"x": 0,
"y": 5
},
{
"x": 1,
"y": 5
},
{
"x": 2,
"y": 5
}
],
"sgf": "(;GM[1]SZ[9]TR[ba][ac][cc][be];B[bc])",
"goal": {
"type": "two-eyes"
},
"hint": "Центральная точка делит пространство на два глаза."
},
{
"id": "tsm-eyes-corner-five",
"kind": "tsumego",
"title": "Пять точек в углу",
"topics": ["глаза", "жизнь и смерть"],
"difficulty": 3,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 0,
"y": 1
},
{
"x": 1,
"y": 1
},
{
"x": 1,
"y": 2
},
{
"x": 1,
"y": 3
},
{
"x": 2,
"y": 3
},
{
"x": 3,
"y": 3
},
{
"x": 3,
"y": 2
},
{
"x": 3,
"y": 1
},
{
"x": 3,
"y": 0
}
],
"setupWhite": [
{
"x": 0,
"y": 2
},
{
"x": 1,
"y": 4
},
{
"x": 2,
"y": 4
},
{
"x": 3,
"y": 4
},
{
"x": 4,
"y": 3
},
{
"x": 4,
"y": 2
},
{
"x": 4,
"y": 1
},
{
"x": 4,
"y": 0
}
],
"sgf": "(;GM[1]SZ[9]TR[bb][db][cd];B[ba])",
"goal": {
"type": "two-eyes"
},
"hint": "Разделите пространство на глаз и глаз."
},
{
"id": "tsm-eyes-side-resist",
"kind": "tsumego",
"title": "Два глаза на стороне",
"topics": ["глаза", "жизнь и смерть"],
"difficulty": 4,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 0,
"y": 1
},
{
"x": 1,
"y": 1
},
{
"x": 2,
"y": 1
},
{
"x": 3,
"y": 1
},
{
"x": 4,
"y": 1
},
{
"x": 4,
"y": 0
}
],
"setupWhite": [
{
"x": 5,
"y": 0
},
{
"x": 5,
"y": 1
},
{
"x": 0,
"y": 2
},
{
"x": 1,
"y": 2
},
{
"x": 2,
"y": 2
},
{
"x": 3,
"y": 2
},
{
"x": 4,
"y": 2
}
],
"sgf": "(;GM[1]SZ[9]TR[ab][cb][ea];B[ba];W[ca];B[da])",
"goal": {
"type": "two-eyes"
},
"hint": "Разделите пространство; на вторжение отвечайте захватом."
},
{
"id": "tsm-eyes-bottom-resist",
"kind": "tsumego",
"title": "Глаза на нижнем краю",
"topics": ["глаза", "жизнь и смерть"],
"difficulty": 4,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 0,
"y": 7
},
{
"x": 1,
"y": 7
},
{
"x": 2,
"y": 7
},
{
"x": 3,
"y": 7
},
{
"x": 4,
"y": 7
},
{
"x": 4,
"y": 8
}
],
"setupWhite": [
{
"x": 0,
"y": 6
},
{
"x": 1,
"y": 6
},
{
"x": 2,
"y": 6
},
{
"x": 3,
"y": 6
},
{
"x": 4,
"y": 6
},
{
"x": 5,
"y": 7
},
{
"x": 5,
"y": 8
}
],
"sgf": "(;GM[1]SZ[9]TR[ah][ch][ei];B[ci];W[bi];B[ai])",
"goal": {
"type": "two-eyes"
},
"hint": "Правильное деление даёт два глаза даже после вторжения."
},
{
"id": "tsm-eyes-tee",
"kind": "tsumego",
"title": "Т-образное пространство",
"topics": ["глаза", "жизнь и смерть"],
"difficulty": 5,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 2,
"y": 0
},
{
"x": 2,
"y": 1
},
{
"x": 3,
"y": 1
},
{
"x": 3,
"y": 2
},
{
"x": 3,
"y": 3
},
{
"x": 4,
"y": 3
},
{
"x": 5,
"y": 3
},
{
"x": 5,
"y": 2
},
{
"x": 5,
"y": 1
},
{
"x": 6,
"y": 1
},
{
"x": 6,
"y": 0
}
],
"setupWhite": [
{
"x": 1,
"y": 0
},
{
"x": 1,
"y": 1
},
{
"x": 2,
"y": 2
},
{
"x": 2,
"y": 3
},
{
"x": 3,
"y": 4
},
{
"x": 4,
"y": 4
},
{
"x": 5,
"y": 4
},
{
"x": 6,
"y": 3
},
{
"x": 6,
"y": 2
},
{
"x": 7,
"y": 1
},
{
"x": 7,
"y": 0
}
],
"sgf": "(;GM[1]SZ[9]TR[db][fb][ed];B[eb])",
"goal": {
"type": "two-eyes"
},
"hint": "Центральная точка превращает Т-форму в два глаза."
}
]

View file

@ -0,0 +1,254 @@
[
{
"id": "tsm-connect-gap",
"kind": "tsumego",
"title": "Закройте щель",
"topics": ["связь"],
"difficulty": 1,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 2,
"y": 2
},
{
"x": 2,
"y": 4
}
],
"setupWhite": [
{
"x": 1,
"y": 3
},
{
"x": 3,
"y": 3
}
],
"sgf": "(;GM[1]SZ[9]CR[cc][ce];B[cd])",
"goal": {
"type": "connect"
},
"hint": "Соедините свои камни до того, как белые разрежут."
},
{
"id": "tsm-connect-cut-stone",
"kind": "tsumego",
"title": "Снимите разрезающий камень",
"topics": ["связь", "разрезание"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 2,
"y": 1
},
{
"x": 2,
"y": 2
},
{
"x": 2,
"y": 4
},
{
"x": 2,
"y": 5
},
{
"x": 1,
"y": 3
}
],
"setupWhite": [
{
"x": 2,
"y": 3
}
],
"sgf": "(;GM[1]SZ[9]CR[cc][ce];B[dd];W[ed];B[cd])",
"goal": {
"type": "connect"
},
"hint": "Сначала снимите вклинившийся камень, потом соединитесь."
},
{
"id": "tsm-cut-wedge",
"kind": "tsumego",
"title": "Вклинивание",
"topics": ["разрезание", "захват"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 4,
"y": 2
},
{
"x": 4,
"y": 4
},
{
"x": 5,
"y": 2
},
{
"x": 5,
"y": 4
},
{
"x": 6,
"y": 3
}
],
"setupWhite": [
{
"x": 3,
"y": 3
},
{
"x": 5,
"y": 3
}
],
"sgf": "(;GM[1]SZ[9]TR[fd];B[ed])",
"goal": {
"type": "capture-target"
},
"hint": "Встаньте между белыми камнями — слабый погибнет."
},
{
"id": "tsm-connect-choice",
"kind": "tsumego",
"title": "Единственная связь",
"topics": ["связь"],
"difficulty": 3,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 1,
"y": 2
},
{
"x": 1,
"y": 3
},
{
"x": 3,
"y": 2
},
{
"x": 3,
"y": 3
}
],
"setupWhite": [
{
"x": 2,
"y": 2
}
],
"sgf": "(;GM[1]SZ[9]CR[bc][dd];B[cd];W[ce])",
"goal": {
"type": "connect"
},
"hint": "Соединяйтесь снизу — сверху стоит белый камень."
},
{
"id": "tsm-connect-atari",
"kind": "tsumego",
"title": "Связь с атари",
"topics": ["связь", "атари"],
"difficulty": 3,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 3,
"y": 3
},
{
"x": 3,
"y": 4
},
{
"x": 5,
"y": 3
},
{
"x": 5,
"y": 4
}
],
"setupWhite": [
{
"x": 4,
"y": 4
}
],
"sgf": "(;GM[1]SZ[9]CR[dd][fe];B[ed](;W[ef])(;W[ii];B[ef]))",
"goal": {
"type": "connect"
},
"hint": "Ход соединяет группы и одновременно ставит атари."
},
{
"id": "tsm-connect-through",
"kind": "tsumego",
"title": "Соединение сквозь клин",
"topics": ["связь", "разрезание"],
"difficulty": 5,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 3,
"y": 1
},
{
"x": 3,
"y": 2
},
{
"x": 3,
"y": 5
},
{
"x": 3,
"y": 6
},
{
"x": 2,
"y": 4
},
{
"x": 4,
"y": 4
},
{
"x": 4,
"y": 2
}
],
"setupWhite": [
{
"x": 3,
"y": 3
},
{
"x": 3,
"y": 4
}
],
"sgf": "(;GM[1]SZ[9]CR[db][dg];B[cd];W[ed];B[fd];W[ii];B[dd];W[ih];B[de])",
"goal": {
"type": "connect"
},
"hint": "Загоните клин в атари, доберите — и заполните брешь."
}
]

View file

@ -0,0 +1,108 @@
[
{
"id": "tsm-yose-edge",
"kind": "tsumego",
"title": "Дожим на первой линии",
"topics": ["ёсэ", "захват"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 7,
"y": 6
},
{
"x": 7,
"y": 7
}
],
"setupWhite": [
{
"x": 8,
"y": 6
},
{
"x": 8,
"y": 7
}
],
"sgf": "(;GM[1]SZ[9]TR[ig][ih];B[if];W[ii];B[hi])",
"goal": {
"type": "capture-target"
},
"hint": "Край помогает: у белых всё меньше дамэ."
},
{
"id": "tsm-joseki-corner",
"kind": "tsumego",
"title": "Наказание в углу 3-3",
"topics": ["дзёсэки", "захват"],
"difficulty": 2,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 2,
"y": 0
},
{
"x": 1,
"y": 1
}
],
"setupWhite": [
{
"x": 0,
"y": 0
},
{
"x": 1,
"y": 0
},
{
"x": 0,
"y": 1
}
],
"sgf": "(;GM[1]SZ[9]TR[aa][ba][ab];B[ac])",
"goal": {
"type": "capture-target"
},
"hint": "Белые ошиблись в дзёсэки — у группы одно дамэ."
},
{
"id": "tsm-yose-hane",
"kind": "tsumego",
"title": "Ханэ в углу",
"topics": ["ёсэ", "захват"],
"difficulty": 3,
"size": 9,
"toPlay": "black",
"setupBlack": [
{
"x": 7,
"y": 8
},
{
"x": 6,
"y": 7
}
],
"setupWhite": [
{
"x": 8,
"y": 7
},
{
"x": 8,
"y": 8
}
],
"sgf": "(;GM[1]SZ[9]TR[ih][ii];B[ig];W[hh];B[hg])",
"goal": {
"type": "capture-target"
},
"hint": "Прижмите к краю и доберите."
}
]