diff --git a/Занимательное программирование/7/3_sokoban/1.slv b/Занимательное программирование/7/3_sokoban/1.slv
new file mode 100644
index 0000000..12c0dda
--- /dev/null
+++ b/Занимательное программирование/7/3_sokoban/1.slv
@@ -0,0 +1,12 @@
+xxxxxxxxxxxxxxxxxxxx
+x x
+x x
+x a x x
+x x x
+x s x x
+x x x
+x 1 xx xxxx
+x ax x
+x x 1 x
+x x x
+xxxxxxxxxxxxxxxxxxxx
diff --git a/Занимательное программирование/7/3_sokoban/2.slv b/Занимательное программирование/7/3_sokoban/2.slv
new file mode 100644
index 0000000..d5b6d8c
--- /dev/null
+++ b/Занимательное программирование/7/3_sokoban/2.slv
@@ -0,0 +1,12 @@
+xxxxxxxxxxxxxxxxxxxx
+x 1 1x
+x x
+x a x
+x a x
+x a s x
+x a x
+x 1 x
+x a x
+x 1 x
+x1 x
+xxxxxxxxxxxxxxxxxxxx
diff --git a/Занимательное программирование/7/3_sokoban/3.slv b/Занимательное программирование/7/3_sokoban/3.slv
new file mode 100644
index 0000000..00664e7
--- /dev/null
+++ b/Занимательное программирование/7/3_sokoban/3.slv
@@ -0,0 +1,12 @@
+xxxxxxxxxxxxxxxxxxxx
+x1x x
+x1x 1 x x
+x x a x x
+x x x
+x x s a x x
+x x x x
+x x x x
+x x a x x
+x a x x
+x x1x
+xxxxxxxxxxxxxxxxxxxx
diff --git a/Занимательное программирование/7/3_sokoban/Sokoban.lpi b/Занимательное программирование/7/3_sokoban/Sokoban.lpi
new file mode 100644
index 0000000..c5e4bca
--- /dev/null
+++ b/Занимательное программирование/7/3_sokoban/Sokoban.lpi
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
diff --git a/Занимательное программирование/7/3_sokoban/Sokoban.lpr b/Занимательное программирование/7/3_sokoban/Sokoban.lpr
new file mode 100644
index 0000000..0ecefd8
--- /dev/null
+++ b/Занимательное программирование/7/3_sokoban/Sokoban.lpr
@@ -0,0 +1,15 @@
+program Sokoban;
+
+{$MODE objfpc}{$H+}
+
+uses
+ Forms, Interfaces,
+ Unit1 in 'Unit1.pas' {Form1};
+
+{$R *.res}
+
+begin
+ Application.Initialize;
+ Application.CreateForm(TForm1, Form1);
+ Application.Run;
+end.
diff --git a/Занимательное программирование/7/3_sokoban/Sokoban.lps b/Занимательное программирование/7/3_sokoban/Sokoban.lps
new file mode 100644
index 0000000..3049fbb
--- /dev/null
+++ b/Занимательное программирование/7/3_sokoban/Sokoban.lps
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Занимательное программирование/7/3_sokoban/Unit1.lfm b/Занимательное программирование/7/3_sokoban/Unit1.lfm
new file mode 100644
index 0000000..0d9094b
--- /dev/null
+++ b/Занимательное программирование/7/3_sokoban/Unit1.lfm
@@ -0,0 +1,160 @@
+object Form1: TForm1
+ Left = 774
+ Height = 576
+ Top = 526
+ Width = 960
+ BorderIcons = [biSystemMenu, biMinimize]
+ BorderStyle = bsSingle
+ Caption = 'Сокобан'
+ ClientHeight = 576
+ ClientWidth = 960
+ Color = clBtnFace
+ DesignTimePPI = 144
+ Font.Color = clWindowText
+ Font.Height = -17
+ Font.Name = 'MS Sans Serif'
+ Menu = MainMenu
+ LCLVersion = '4.6.0.0'
+ OnKeyDown = FormKeyDown
+ OnShow = FormShow
+ object Screen: TImage
+ Left = 0
+ Height = 576
+ Top = 0
+ Width = 960
+ Align = alClient
+ end
+ object BackBuffer: TImage
+ Left = 36
+ Height = 576
+ Top = 36
+ Width = 960
+ Visible = False
+ end
+ object MainMenu: TMainMenu
+ object FileMenu: TMenuItem
+ Caption = 'Файл'
+ object ItemOpen: TMenuItem
+ Caption = 'Открыть'
+ OnClick = ItemOpenClick
+ end
+ object ItemExit: TMenuItem
+ Caption = 'Выход'
+ OnClick = ItemExitClick
+ end
+ end
+ end
+ object ImageList: TImageList
+ Height = 32
+ Width = 32
+ Left = 84
+ Top = 12
+ Bitmap = {
+ 4C7A120000002000000020000000540C00000000000078DAED5D79AC5F4515FE
+ 416929A4C866444005A906D08A2D90B049A1205048150B84C568341291402085
+ 420394D85B903F301521981051D0A4568D60D8A4A041FBA0153454446CA0C822
+ 2D7BA12C852A7D409F33E59B3A6FDECCDC997BE7CCDCBE774E72D2BE3BF7CE37
+ EB99F3CDF6EBF57A033D5656565656565656565656565656565656D64E69E5D0
+ 5C98030EA54A8B0FD397161A6C97A44F431584EB4F4745861D12D63C0D95337E
+ 579DBBDF4B875FD7EED2E037C34E978630FCBAE729F1C3EBDAF5DDA68D5FBAFC
+ F3B6BFD2FDAF5BF6A78CFD2D3DFE7461FCED82FFD105FFAB0BFE272B2BABD2BE
+ BEBE8DFFBA940253A914571A94A44A832F7E5DABAADAA0B6F2C981AFDE9769B0
+ 954D2C9ECA8BAF9E6DE9309FC5D6852D5F322D66BE7571A52DA60C54FDE97977
+ 95A5994F332D66FB34D3E8CBB39E8E90E7BEB2E8C161A9EB1766FCB67FCDB49A
+ 79B5958BAFEDD8B0F57277E53554F57663E299FD45D7D0F6EC6A0375EFE9F6A1
+ 0BF8663D9975DF545DEDC365AF4CFC2678B63CDBDABFB20D66BE75FB9DA20C42
+ EBBF6E2C75955FE83BA6D8FA83D9FE7CEDC0AC37D778A4F73F5FFBD3ED8BAD4D
+ C4968B2F7D2EFC3AFB178A5F67A36D6340697C57FBD7E333DF6DDB27EAC65F13
+ A78D5D32FD8750BF23B44FFBD2646B8F757EB5996F57FBB0A531A49C42FC30D7
+ F7B6B4B5A9F73A7EA1DB485FFBAB6B3FB17EB82D3ED3378E298B26BE7F2AFC36
+ FCC3E59398BE2135FFF3D5873E9EB332FF67FEDF9EFFDB7CC40F9ED9E75F53D8
+ 7DB71D899B7F6ECBFF87E6377EFEDDD747EAF2ECC38E5D7F08E917B6F123D5FA
+ 4B13FE9F7AFD2996FFA75E7FF3F19F18FCA6EB8F75FC6B70B9D0ACBF86F27FAA
+ F5E770FE4FB3FE1ECAFFA9F043F93F55F987F37FDAF617C2FF29FA5F0CFFA7B0
+ 3F31FC9FC2FEC6F07F8AF12796FF9B7DA1C9F8DB84FFDB7D9F78FFA32917B5CD
+ 4134F1BF52F002B73F365429F8BF692BCC30E6FFCCFF878DCA32CCA19B68F9D8
+ D64243C2183FBC6F0FE581F6BEAF87FBC242E20E52F94D8C26CEFF863843FB57
+ C2FC6F7C3FB28FC7E2D4AD6FC6E2D7ADE1740DBFAE5CDAE0B7297F67FD9BBE9F
+ A7FEDB94BF756C72F99F8E3126297E1D076E881F3C363B38910FBF4DFD0FC2EF
+ F9F9D0A0345094BF877F94C61FD20E29EABF213E75FDA7C87F1A7C8427C41F62
+ FFBC69A81ADBBF60FBEFE4E1D520FC54F6DF3AFEB8F6DFE71C7F3DD829C7DF8D
+ F518E97F35F5B3BAE6FF35F28109FCFF14F30614710F17DD24CABF64FB2BCDBF
+ 88ED4FBCFDF5FBFFA4F63FC0FF27E35F81FE3F19FF0AF4FF49F8D730F6FFD9FF
+ 1ED9FE7F1CFFAEF7FF49EA3FC2FF279BFF08F4FFC9F857A0FF4FC6BF32CFBF8D
+ F4F9CF5CF3CF9DE75F85D71F8AAFBFF0FA5B717C5E7F66CD391F61DF1BD9F39E
+ D1F2FD6D8BF7FFBE25E3337E41BB17B1FF2AD4C6077FD741FC4AF76552D97D97
+ 6F04FC213E4A2876C87AB4C73FEC59CEA96C68AF75F8A17B4103F1A3F2EFE1E1
+ D6FDB001F8D6FA7762D7E33BD31080EFF5F923F6E35AD7E693E037D8139CBCFC
+ 63D25011B4BF0825E97F0DB053E53F763EA669FFCB687F79FCEB8EFFC1FEE708
+ C22FCDBF5899EB8FB4B6AEF579C62F8BDF25CE4FEE6B74199FC8DF0CAA7F2FD7
+ 88E4F78EBD3883D63E42F09BF27B03DFBAF65687DF86DFB7CD7F5B7E6FC18FAA
+ FFB6FCDEC7B582F15BF0FB64F80DF97DDBF26FCBEF93F5BF96D86DFA5F1B7EDF
+ 24FFA5ED2F8F7FC5FC0FF63F199FB97E19ED8234BDBFD576B76FCCFDCC21DFEB
+ E3A71A4F6CDF57C67B4DD26FC6DF34FF1BEFF0F1DCC3DB35FC21F7A404A423B6
+ FEBBFABD6D7F5AA8A6F8BE6B7DDFD5C7A8EAC8D7D6CC366ABD0FA8E5F7BEFE67
+ BDBFDC75C78EF6BDAB8F877E9F13BFCA54FE5DB411B9FA7857FB7FD7ED77AFAA
+ 92F90FB1E37FDD18D874FC0E1DFF73E257015894B69DC7FF6EF6FD2E8FFFA69F
+ 6BCB93EBFB9E631C0B1D7F2BCFF775362AC5F89F12BF6D1F4DE57F8DC4F1BFC7
+ C2C2B2C989E8D2E7097D1743CE5E1971B7117A93318CEF9509FBB342970B7D0F
+ F97F2733FE2D425F117A04FECE8D3F47E86EDADFEFE4AE7F233D8CCFF88CCFF8
+ C31E5F608C17BAD4D0F5C05F663C1F4F803F2182564F600F8985858585856558
+ F1FE8942E70B7D5668BFD03785FE157C783431F6D7B53987D7843E20F409CDEF
+ 582C742CA1FFA5FCBDEF0BDD520B3B42E85A845D48847F05E2BFDF11FE3D842F
+ 21C29F8C3A3ECA117E22F09F2AD42ECFA0CC7F0DF66642FF0CFCF30AE0CF02F6
+ E354EDDF837D2178C86AA17B67C41D25F43AE4FB45A1FB64C4FE90D0BB35EEB5
+ 5B46EC6D853E04ECDFCBB464C4DE5A6BE70BA8EDBD057F01B0E53CE8A8CCD853
+ 81BD52E8B8027DFC0FC05F65990730F56304F84B23F8FFA7D853626161616161
+ 1956FCFF30A1BF85CFFD2EF8FF5F84CEA4E61E22FED99A8FF1027CD127B5677F
+ 93FB0388B00F01CF795FE8E992F319DC780DD2309708FFE788FF278EF0B9087F
+ 80087F8AD0EFB8D676C4F36F51E207A4EFD7C0BF3A23A6DC8B3249E88DC0FE97
+ D01D33E0EE6EF8DBB20F5C928B97089C5DC07B9F810D90FDE2EF424F2E50EFA3
+ 857E43E8CB288B8B0BDA4529FF15BA5DA134BC8E344C21986B91F38B3708FDA4
+ E71D353F7A3841DE949D9FED083F54EB0F1F25C03F1F71CB3C9E6AD8FF89E8FB
+ 52EE22AADB51187795BC8C79F7A7B57D004F53707F231D270B5D087CD9F7DFC2
+ B8FBDD9C73512C2C2C2C2C2C2C45E7032683134B392933F638F83C0385F0AF07
+ EEF3B9F105D671C0BC5DE8CD39F105CE0E987F916BBE3B17C0575CFF14FC9D0D
+ 5F6202EB57DAB32CF828EBD5686FDB17C0BF1338538DE7E4F822EE6F03E33A4B
+ 1829BEE4BCE0398FCA35F002F833238F35FF2231FE979047973EA7E6FEF0F7B9
+ 99EDF0CD25EC3FE377039F858585858585856C8C1F1BE87B7E9390FB0C60CDC1
+ B7F7711A11FEDEC07FA650F91F04FC870BE11FABAE172A847F1AF06F2D847F16
+ F0E5DCC345B21E84FE07E73EE4FF2F16BA1521FE25463F93EB8D8BB006AF44EE
+ 0BDF9608FF52A12F619DFF2023EC286DFD7D7EA1FA3901F8722EF2C305F04761
+ ED5FCAD185CAE059E04F27887B73ECFB18ED08DF529B073E98007F25E23EDD11
+ AECEFFACD1CF6625C457E7AF5E37F75760EFC76A845F4638FEF6697D5D9E7BFB
+ 93D0C7B46792876F41DCC6E5DE877B501FF2FCDFAB388732684F040B0B0B0B0B
+ 0BCBB09B0B982EF48F38832FD7C51E01271A97017B1E7C9DF5D87778BFE6772F
+ 26E6606AED57AE774DD49EEF82B541293389B037D3F6C04EB1841F867DE8A711
+ E11F5078FE419D71FF41217C75FE429E81F88C5CFF071F5E27EF3C40BBDC8110
+ 5F9D379F8739871538072DE700DED638C14788F0176BFCFA4A9D67E03CC23273
+ 5F4462FC7B11FF3F2517B5844F46F87B14732022CE3B10FF8F3DFD53D9A14308
+ F0AF41DC3FF5BCB3826AFE41C4F935DFFD1AE086FD78671201FEF6DAF9824996
+ F0AF20EC2DAA7B09B4B147CEF7EDAA3D1FAFED439B476803C6A2BFAB73268B70
+ 064E95FB12CAF10F69D802F7C03C04BBB316FFBF8062DE85858585858585A528
+ E75F52F2FE1FE9F706DC7BA4D69F3E51A07C4EA1E43F01BEB1E4A26F509C7D0D
+ C057F7B0CD2880FD3970BEE5946B5F1EFCBB90F7E30B601F08ECA5856CC2EDC0
+ FF6A01ECDDD1DF5F29C13BE44F1A21EFD7162A7B35DF724C01EC5D81BDAE50D9
+ 2B5BFB8F42657F39F07F59087F3EF0AF29DCEF2F67CF8885858585856544CC03
+ 1C29F7601BF7FFC9739F33848E21C69EABADFD2FC27D54B7200D521EA4BA8348
+ C4BBBF76FEE070236C47DCBD2EE54A22FC3988FF6E47F8D9087F8498EF2C7084
+ ABFDF9CB88F08F56BF2F606B67E2D90F117E7D06BF57B6B97DB01E27F77E9C8B
+ 35B81729E79E70D7DD2CCCF3987203F5BC177EE7E209F4BFFBB0DFE40EEC8996
+ EB805751DDCB8E3BD6FA81F3054BFFEBA3BCFF51BB7BAD72847F1AE1FD147740
+ 6AE73C8EF1BCA3ECE0A104F8AB10F7973DEFA876F945027CB5FFE48A9AF297F2
+ 71027C75BFE95AF37C037E936121C2EF216A7F727FC9CFB4F14FF5BF5B310736
+ 8036426D03E4DEBFDF69F7FFADC1FAFF1CAAB3472C2C2C2C2C2C2CC5F9BF5C73
+ FC0DEE7FEB871F7093D07D33609FA9ADF1AFC4F9C7151A2F9F4E883D41FBEDC1
+ 19C6FDCFE76B7B4F7722C2FF510DFF5F487CFEF441B5FFDD113E8D724D4EFB9D
+ C7698EF03DB5763086007F694DFEF7D0F8C71E04F86A8FC59D8EF07334FCCF13
+ E0EFA7DD337E86113615DC731DC2F7239E031BC0799BDB701E60006BB2EA0CF6
+ 9E8476E024EC857A1B79BE17770F8CC13E948112F7FF633E6AC3EFA2141A1766
+ F9E60713C47F30F680CDB6846D25F4DFC03F8EB07CD7A38D1FAB3DDF1AE3A194
+ FB88CBF82AADFD3FAC9DC153BF01B273867A3E156D7E15CE202CC7DD08DBF458
+ 58585822E47F110951EA
+ }
+ end
+ object OpenDialog: TOpenDialog
+ Filter = 'Уровни Сокобана (*.slv)|*.slv'
+ Left = 144
+ Top = 12
+ end
+end
diff --git a/Занимательное программирование/7/3_sokoban/Unit1.pas b/Занимательное программирование/7/3_sokoban/Unit1.pas
new file mode 100644
index 0000000..bdb2a92
--- /dev/null
+++ b/Занимательное программирование/7/3_sokoban/Unit1.pas
@@ -0,0 +1,271 @@
+unit Unit1;
+
+{$MODE objfpc}{$H+}
+
+interface
+
+uses
+ LCLIntf, LCLType, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+ Dialogs, ExtCtrls, Menus, ImgList;
+
+type
+ TForm1 = class(TForm)
+ MainMenu: TMainMenu;
+ FileMenu: TMenuItem;
+ ItemOpen: TMenuItem;
+ ItemExit: TMenuItem;
+ Screen: TImage;
+ ImageList: TImageList;
+ OpenDialog: TOpenDialog;
+ BackBuffer: TImage;
+ procedure ItemExitClick(Sender: TObject);
+ procedure ItemOpenClick(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ procedure FormKeyDown(Sender: TObject; var Key: Word;
+ Shift: TShiftState);
+ private
+ { Private declarations }
+ public
+ { Public declarations }
+ end;
+
+const
+ WALL = 11; SPACE = 10; BOULDER = 1;
+ SL = 14; SR = 15; SU = 12; SD = 13; { Индексы спрайтов. }
+ BOULDER_SET = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];
+ PLACE_SET = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];
+var
+ Form1: TForm1;
+ Field: array[1..20, 1..12] of Integer; { Игровое поле. }
+ Places: array[1..20, 1..12] of Integer; { Положение мест. }
+ PlaceCount: Integer; { Общее число мест на уровне. }
+ CurX, CurY: Integer; { Текущее положение Сокобана. }
+ Positioned: Integer; { Количество размещенных камней. }
+ Busy: Boolean = false; { Статус обработчика клавиш. }
+
+implementation
+
+{$R *.lfm}
+
+{ Перевод символа в порядковый номер картинки. }
+function SymbolToCode(c: Char): Integer;
+begin
+ case c of
+ 'x': SymbolToCode := WALL; { 'x' - стена }
+ ' ': SymbolToCode := SPACE; { ' ' - пустое пространство }
+ 's': SymbolToCode := SL; { 's' - стартовая локация }
+ else
+ SymbolToCode := -1 { Неизвестный символ. }
+ end;
+end;
+
+{ Загрузить уровень из файла. }
+procedure LoadLevel(FileName: String);
+var f: File of Char;
+ i, j : Integer;
+ c: Char;
+begin
+ AssignFile(f, FileName); { Открыть файл. }
+ Reset(f);
+
+ PlaceCount := 0; { Подсчет количества мест. }
+ for j := 1 to 12 do { Цикл по строкам. }
+ begin
+ for i := 1 to 20 do { Цикл по элементам строки. }
+ begin
+ Read(f, c); { Считаем текущий эелемент }
+ if c in PLACE_SET then { если это "место" }
+ begin
+ Field[i, j] := SPACE; { в поле равнозначно пустому элементу }
+ VAL(c, Places[i, j]); { True в массиве Places }
+ Inc(PlaceCount)
+ end
+ else if c in BOULDER_SET then
+ begin
+ Places[i, j] := 0;
+ Field[i, j] := ORD(c) - ORD('`')
+ end
+ else
+ begin
+ Places[i, j] := 0; { иначе НЕ "место" }
+ Field[i, j] := SymbolToCode(c); { определяем код элемента }
+
+ if c = 's' then { если текущая локация - }
+ begin { стартовая, запоминаем }
+ CurX := i; { ее координаты }
+ CurY := j
+ end
+ end
+ end;
+ Read(f, c); { считываем и пропускаем возврат каретки }
+ Read(f, c) { считываем и пропускаем перевод строки }
+ end;
+
+ CloseFile(f) { закрыть файл }
+end;
+
+{ Перерисовка уровня. }
+procedure RedrawField;
+var
+ i, j: Integer;
+ code: Integer;
+ bitmap: TBitmap;
+ ScreenRect: TRect;
+begin
+ bitmap := TBitmap.Create; { объект для временного хранения рисунка }
+ Positioned := 0; { считаем, что размещено 0 камней }
+
+ for j := 1 to 12 do
+ for i := 1 to 20 do
+ begin
+ code := Field[i, j]; { код текущего элемента }
+
+ { Пара "пустое пространство" / "место" означает "место" }
+ if Field[i, j] = WALL then
+ code := 8
+ else if Field[i, j] = SU then
+ code := 4
+ else if Field[i, j] = SD then
+ code := 5
+ else if Field[i, j] = SL then
+ code := 6
+ else if Field[i, j] = SR then
+ code := 7
+ else if (Field[i, j] = SPACE) and (Places[i, j] <> 0) then
+ begin
+ Form1.ImageList.GetBitmap(0, bitmap);
+ Form1.BackBuffer.Canvas.Draw((i - 1) * 32, (j - 1) * 32, bitmap);
+ code := 8 + Places[i, j]
+ end
+ else if Field[i, j] = SPACE then
+ code := 0
+ { а пара "камень" / "место" - "камень на месте" }
+ else if Field[i, j] = Places[i, j] then
+ begin
+ code := 3;
+ { При этом увеличиваем счетчик размещенных камней. }
+ Inc(Positioned)
+ end
+ else if Field[i, j] > 0 then
+ begin
+ Form1.ImageList.GetBitmap(2, bitmap);
+ Form1.BackBuffer.Canvas.Draw((i - 1) * 32, (j - 1) * 32, bitmap);
+ code := 8 + Field[i, j]
+ end;
+
+ Form1.ImageList.GetBitmap(code, bitmap); { Достаем картинку }
+ { и рисуем ее на соответствующем месте виртуального экрана. }
+ Form1.BackBuffer.Canvas.Draw((i - 1) * 32, (j - 1) * 32, bitmap)
+ end;
+
+ bitmap.Free;
+
+ { Копируем содержимое виртуального экрана на основной. }
+ ScreenRect := Rect(0, 0, 640, 384);
+ Form1.Screen.Canvas.CopyRect(ScreenRect, Form1.BackBuffer.Canvas, ScreenRect)
+end;
+
+{--------------------------------------------------------------------------}
+
+procedure TForm1.ItemExitClick(Sender: TObject);
+begin
+ Application.Terminate
+end;
+
+{--------------------------------------------------------------------------}
+
+procedure TForm1.ItemOpenClick(Sender: TObject);
+begin
+ if OpenDialog.Execute then
+ begin
+ LoadLevel(OpenDialog.FileName);
+ RedrawField
+ end;
+end;
+
+{--------------------------------------------------------------------------}
+
+procedure TForm1.FormShow(Sender: TObject);
+begin
+ while not OpenDialog.Execute do
+ ;
+
+ LoadLevel(OpenDialog.FileName);
+ RedrawField
+end;
+
+{--------------------------------------------------------------------------}
+
+function PositionBy(x, y: Integer; scale: Integer = 1): Integer;
+begin
+ Result := Field[CurX + x * scale, CurY + y * scale]
+end;
+
+procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
+ Shift: TShiftState);
+var
+ dx, dy: Integer; { Смещения Сокобана (куда идем). }
+ SprIdx: Integer; { Текущий номер его спрайта. }
+begin
+ if Busy then Exit; { Если обработчик занят, выходим. }
+
+ Busy := true; { статус = занято }
+ case Key of
+ VK_LEFT:
+ begin { идем влево }
+ dx := -1;
+ dy := 0;
+ SprIdx := SL
+ end;
+ VK_RIGHT:
+ begin { вправо }
+ dx := 1;
+ dy := 0;
+ SprIdx := SR
+ end;
+ VK_UP:
+ begin { вверх }
+ dx := 0;
+ dy := -1;
+ SprIdx := SU
+ end;
+ VK_DOWN:
+ begin { вниз }
+ dx := 0;
+ dy := 1;
+ SprIdx := SD
+ end;
+ else
+ { никуда не идем }
+ dx := 0;
+ dy := 0;
+ SprIdx := Field[CurX, CurY]
+ end;
+
+ if PositionBy(dx, dy) = SPACE then { если целевая клетка пуста }
+ begin
+ Field[CurX + dx, CurY + dy] := SprIdx; { переходим в нее }
+ Field[CurX, CurY] := SPACE; { на старой позиции теперь ничего нет }
+ CurX := CurX + dx;
+ CurY := CurY + dy;
+ end
+ { если целевая клетка содержит камень, а клетка, следующая за ней, пуста }
+ else if (PositionBy(dx, dy) = BOULDER) and (PositionBy(dx, dy, 2) = SPACE) then
+ begin { двигаем камень: }
+ Field[CurX + dx, CurY + dy] := SprIdx; { новая позиция Сокобана }
+ Field[CurX + 2 * dx, CurY + 2 * dy] := BOULDER; { новая позиция камня }
+ Field[CurX, CurY] := SPACE; { на старой позиции - пусто }
+ CurX := CurX + dx;
+ CurY := CurY + dy;
+ end;
+
+ RedrawField; { перерисовываем уровень }
+ { Если все камни размещены }
+ if Positioned = PlaceCount then
+ Application.MessageBox('Уровень пройден!', 'Sokoban', MB_ICONEXCLAMATION);
+
+ Application.ProcessMessages;
+ Busy := false; { статус = свободно }
+end;
+
+end.
diff --git a/Занимательное программирование/7/3_sokoban/graphics--numbers.png b/Занимательное программирование/7/3_sokoban/graphics--numbers.png
new file mode 100644
index 0000000..e089747
Binary files /dev/null and b/Занимательное программирование/7/3_sokoban/graphics--numbers.png differ
diff --git a/Занимательное программирование/7/3_sokoban/graphics.bmp b/Занимательное программирование/7/3_sokoban/graphics.bmp
new file mode 100644
index 0000000..e6b59c5
Binary files /dev/null and b/Занимательное программирование/7/3_sokoban/graphics.bmp differ
diff --git a/Занимательное программирование/README.txt b/Занимательное программирование/README.txt
index 0d47ec2..09b4801 100644
--- a/Занимательное программирование/README.txt
+++ b/Занимательное программирование/README.txt
@@ -18,3 +18,7 @@ Lazarus.
Шестая глава "Алгоритмы на графах" отсутствует, поскольку содержит
только теоретические задания.
+
+Седьмая глава "Простые компьютерные игры" применяет Lazarus. Изображения
+для частично сделаны заново, частично скопированы из приложения книги
+и отредактированы.