From b06e49f6f3882cbef9512875c2c660a166287162 Mon Sep 17 00:00:00 2001 From: uswebk Date: Tue, 28 Jul 2026 23:26:57 +0900 Subject: [PATCH] Add data-013 to data-016 log extraction problems Co-Authored-By: Claude Opus 5 --- problems/data/013-extract-ip.json | 50 ++++++++++++++++++++ problems/data/014-keep-error-lines.json | 55 ++++++++++++++++++++++ problems/data/015-extract-status-code.json | 53 +++++++++++++++++++++ problems/data/016-keep-first-field.json | 54 +++++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 problems/data/013-extract-ip.json create mode 100644 problems/data/014-keep-error-lines.json create mode 100644 problems/data/015-extract-status-code.json create mode 100644 problems/data/016-keep-first-field.json diff --git a/problems/data/013-extract-ip.json b/problems/data/013-extract-ip.json new file mode 100644 index 0000000..7c93dab --- /dev/null +++ b/problems/data/013-extract-ip.json @@ -0,0 +1,50 @@ +{ + "id": "data-013", + "category": "data", + "difficulty": 1, + "start": [ + "10.0.0.5 - - [18/Jul/2026:10:22:31] \"GET /index.html\" 200" + ], + "goal": [ + "10.0.0.5" + ], + "solutions": [ + { + "keys": "WD", + "optimal": true + }, + { + "keys": "f-d$" + } + ], + "tags": [ + "W", + "D", + "delete", + "log" + ], + "i18n": { + "ja": { + "title": "アクセスログから IP だけ残す", + "description": "先頭の IP アドレスだけを残して 10.0.0.5 にしてください", + "hints": [ + "D はカーソル位置から行末までを削除する。行末の空白は判定で無視される" + ], + "notes": [ + "W で 2 つ目の WORD の先頭へ進み、D で行末まで削除する", + "f- で最初の - へ飛んでから d$ で行末まで削除する" + ] + }, + "en": { + "title": "Keep only the IP from an access log", + "description": "Delete everything after the leading IP address to get '10.0.0.5'", + "hints": [ + "D deletes from the cursor to the end of the line; trailing whitespace is ignored when checking" + ], + "notes": [ + "W moves to the start of the second WORD, then D deletes to the end of the line", + "f- jumps to the first '-', then d$ deletes to the end of the line" + ] + } + } +} diff --git a/problems/data/014-keep-error-lines.json b/problems/data/014-keep-error-lines.json new file mode 100644 index 0000000..0d9484b --- /dev/null +++ b/problems/data/014-keep-error-lines.json @@ -0,0 +1,55 @@ +{ + "id": "data-014", + "category": "data", + "difficulty": 3, + "start": [ + "INFO server started", + "ERROR failed to connect", + "DEBUG cache warm up", + "ERROR timeout on /api/users", + "INFO request handled" + ], + "goal": [ + "ERROR failed to connect", + "ERROR timeout on /api/users" + ], + "solutions": [ + { + "keys": ":v/ERROR/d", + "optimal": true + }, + { + "keys": ":g!/ERROR/d" + } + ], + "tags": [ + ":v", + ":g", + "global", + "log" + ], + "i18n": { + "ja": { + "title": "ERROR 行だけ抜き出す", + "description": "ERROR を含まない行をすべて削除して ERROR の 2 行だけ残してください", + "hints": [ + ":g はパターンに一致した行、:v は一致しなかった行に対してコマンドを実行する" + ], + "notes": [ + ":v/ERROR/d は ERROR を含まない行をすべて削除する", + ":g!/ERROR/d は :v とまったく同じ意味。g に ! を付けると一致しない行が対象になる" + ] + }, + "en": { + "title": "Extract only the ERROR lines", + "description": "Delete every line that does not contain ERROR, leaving the two ERROR lines", + "hints": [ + ":g runs a command on matching lines, :v on the lines that do not match" + ], + "notes": [ + ":v/ERROR/d deletes every line without ERROR", + ":g!/ERROR/d means exactly the same thing: the ! inverts the match" + ] + } + } +} diff --git a/problems/data/015-extract-status-code.json b/problems/data/015-extract-status-code.json new file mode 100644 index 0000000..a86b562 --- /dev/null +++ b/problems/data/015-extract-status-code.json @@ -0,0 +1,53 @@ +{ + "id": "data-015", + "category": "data", + "difficulty": 3, + "start": [ + "10:22:31 GET /api/users status=200", + "10:22:35 POST /api/login status=401", + "10:22:40 GET /api/items status=500" + ], + "goal": [ + "200", + "401", + "500" + ], + "solutions": [ + { + "keys": ":%s/.*status=//", + "optimal": true + }, + { + "keys": ":%normal! $F=d0x" + } + ], + "tags": [ + ":s", + "substitute", + "log" + ], + "i18n": { + "ja": { + "title": "ステータスコードだけ取り出す", + "description": "各行から status= までを削除してコードの数字だけにしてください", + "hints": [ + ".* は最長一致なので、行頭から status= までをまとめて飲み込める" + ], + "notes": [ + ":%s/.*status=// は各行の先頭から status= までを削除する", + ":%normal! $F=d0x は各行で = まで戻り、d0 で手前を削除、x で = 自体を消す" + ] + }, + "en": { + "title": "Pull out just the status code", + "description": "Delete everything up to and including 'status=' on each line, leaving only the code", + "hints": [ + ".* is greedy, so it swallows the whole line up to 'status='" + ], + "notes": [ + ":%s/.*status=// removes the start of each line through 'status='", + ":%normal! $F=d0x goes back to the '=' on each line, d0 removes what precedes it, and x deletes the '=' itself" + ] + } + } +} diff --git a/problems/data/016-keep-first-field.json b/problems/data/016-keep-first-field.json new file mode 100644 index 0000000..3725354 --- /dev/null +++ b/problems/data/016-keep-first-field.json @@ -0,0 +1,54 @@ +{ + "id": "data-016", + "category": "data", + "difficulty": 3, + "start": [ + "10.0.0.5 GET /index.html 200", + "10.0.0.7 GET /assets/app.js 404", + "10.0.0.9 POST /api/login 500" + ], + "goal": [ + "10.0.0.5", + "10.0.0.7", + "10.0.0.9" + ], + "solutions": [ + { + "keys": ":%s/ .*//", + "optimal": true + }, + { + "keys": ":%normal! WD" + } + ], + "tags": [ + ":s", + ":normal", + "substitute", + "log" + ], + "i18n": { + "ja": { + "title": "全行から IP 列だけ残す", + "description": "各行の最初の空白以降を削除して IP アドレスだけにしてください", + "hints": [ + "最初の空白から行末までを 1 回の置換で消せる" + ], + "notes": [ + ":%s/ .*// は各行の最初の空白から行末までを削除する。置換は各行 1 回目の一致だけなので g は不要", + ":%normal! WD は各行で 2 つ目の WORD へ移動して行末まで削除する" + ] + }, + "en": { + "title": "Keep only the IP column", + "description": "Delete everything from the first space onward on each line, leaving just the IP addresses", + "hints": [ + "One substitution can wipe from the first space to the end of the line" + ], + "notes": [ + ":%s/ .*// deletes from the first space to the end of each line; no g flag is needed since only the first match matters", + ":%normal! WD moves to the second WORD on each line and deletes to the end" + ] + } + } +}