From 2338c7279b51d903e9e30a44d235e8a8d55afe98 Mon Sep 17 00:00:00 2001 From: zurabi Date: Mon, 3 Oct 2022 19:21:05 +0400 Subject: [PATCH 1/5] implemented solution and explanation --- problem-set/average15.java | 16 ++++++++++ problem-set/average15Explanation.md | 45 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 problem-set/average15.java create mode 100644 problem-set/average15Explanation.md diff --git a/problem-set/average15.java b/problem-set/average15.java new file mode 100644 index 0000000..8f20b83 --- /dev/null +++ b/problem-set/average15.java @@ -0,0 +1,16 @@ + +import acm.program.*; + +public class average15 extends ConsoleProgram { + + public void run() { + getAverage(); + } + + // reads 2 integers from console and outputs their average + private void getAverage() { + int a = readInt("Enter number #1: "); // read first integer + int b = readInt("Enter number #2: "); // read second integer + println((double) (a + b) / 2); // output average + } +} diff --git a/problem-set/average15Explanation.md b/problem-set/average15Explanation.md new file mode 100644 index 0000000..f80feb8 --- /dev/null +++ b/problem-set/average15Explanation.md @@ -0,0 +1,45 @@ +# 15.average + +ამოცანა: +კონსოლიდან წაიკითხეთ ორი მთელი რიცხვი და დაბეჭდეთ მათი საშუალო არითმეტიკული + +##ამოხნის გზა +* ნაწილი 1: ვიღებთ მომხმარებლისგან ინფორმაციას +* ნაწილი 2: ვამუშავებთ ამ ინფორმაციას დავალების მიხედვით, ვკრებთ და შუაზე ვყოფთ. + +--- +მომხმარებლისგან მთელი რიცხვის წაკითხვა შესაძლებელია `readInt()` ფუნქციის საშუალებით. +შესაძლებელია მომხმარებელს მესიჯიც გამოვუტანოთ, თუ რა ტიპის ინფორმაცია უნდა შეიყვანოს. +--- +მომხმარებლის მიერ ინფორმაციის შემოსვლის შემდეგ დავამუშავოთ რიცხვები, შევკრიბოთ და შუაზე გავყოთ. +შედარებით ბევრი ოპერაცია რომ იყოს გასაკეთებლი, უმჯობესი იქნებოდა თუ ცალკე მეთოდად გავიტანდით, +თუმცა ამ შემთხვევაში ამის საჭიროება არ არის. +--- + +## რა ტიპის შეცდომები შეიძლება დავუშვათ იმპლემენტაციისას +1. +მთავარი შეცდომა რაც შეიძლება ამ ამოცანაში დავუშვათ, არის პირდაპირ შემოსული ორი რიცხვის შეკრება და შუაზე გაყოფა. +ერთი შეხედვით თითქოს ამან სწორად უნდა იმუშავოს, მაგრამ არ ვითვალისწინებთ, რომ მომხმარებლის მიერ შემოსული ინფორმაცია +მთელ რიცხვებშია (`int` ტიპისაა) ჩაწერილი. თუ ორი რიცხვიდან არცერთია მეორეს ჯერადი და ჩვენ მათ ერთმანეთზე გაყოფას შევეცდებით, +პროგრამა ავტომატურად ქვემოთ დაამრგვალებს მიღებულ შედეგს. მაგალითად: თუ 5ს გავყოფთ 2-ზე მივიღებთ 2.5-ის ქვემოთ +დამრგვალებულ რიცხვს, ანუ 2, რაც ჩვენთვის არასასურველ პასუხს მოგვცემს. +ამ პრობლემის გადაჭრა cast-ის საშუალებით შეგვიძლია (cast არის ტერმინი და არა მეთოდი). cast-ის საშუალებით შეგვიძლია +კომპილატორს "ვუთხრათ", რომ კონკრეტული ცვლადი განიხილოს ისეთ ტიპად, როგორიც გვჭირდება. მაგალითად , +თუ `int` ტიპის ცვლად a - ს cast-ის საშუალებით `double` ტიპს მივანიჭებთ (`double(a)`), +ის განიხილება როგორც ათწილადი და არა მთელი რიცხვი. + +2. +კიდევ ერთი შეცდომა რაც შეიძლება ამ დავალებაში დავუშვათ არის შემდეგი: `println(double((a+b)/2))`. თითქოს ზემოთ აღწერილი +შეცდომა გავითვალისწინეთ და cast გამოვიყენეთ ათწილადში გადასაყვანად, თუმცა პასუხს არასწორს მივიღებთ. ამის მიზეზი კი არის ის, +თუ რა მიმდევრობით ხდება ოპერაციების გაკეთება. განვხილოთ შემთხვევა a = 5, b = 2: პირველ რიგში შეიკრიბება a და b. +მიღებული პასუხი, 7, `int` ტიპის იქნება. შემდეგ გაიყოფა ორზე, და რადგან ორივე რიცხვი `int` ტიპის იყო, ქვემოთ დამრგვალდება, +მივიღებთ 3-ს. მხოლოდ ამის შემდეგ მოხდება cast და 3 განიხილება როგორც ათწილადი (`double`), დაიბეჭდება პასუხი 3.0. + +## სწორი ამონახსნი +სწორ ამონახსნს მხოლოდ იმ შემთხვევაში მივიღებთ, თუ ამ ორი რიცხვიდან ერთ-ერთს ან მათ ჯამს განვიხილავთ ათწილადად. +* გზა 1. cast გამოვიყენოთ a-ზე; +* გზა 2. cast გამოვიყენოთ b-ზე; +* გზა 3. cast გამოვიყენოთ (a+b)-ზე; + +მთავარია რიცხვი განვიხილოთ double ტიპად, სანამ მას 2-ზე გავყოფთ. პასუხი გამოვიტანოთ `println()` +ფუნქციით. \ No newline at end of file From e731bef66b008e5f4b2854e12da89c1cc37a64cb Mon Sep 17 00:00:00 2001 From: zurabi Date: Mon, 17 Oct 2022 16:21:35 +0400 Subject: [PATCH 2/5] added implementation and explementation problem.12 house --- problem-set/FillRaw.md | 105 ---------------------------------------- problem-set/house.java | 71 +++++++++++++++++++++++++++ problem-set/houseExp.md | 88 +++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 105 deletions(-) delete mode 100644 problem-set/FillRaw.md create mode 100644 problem-set/house.java create mode 100644 problem-set/houseExp.md diff --git a/problem-set/FillRaw.md b/problem-set/FillRaw.md deleted file mode 100644 index 792b9f3..0000000 --- a/problem-set/FillRaw.md +++ /dev/null @@ -1,105 +0,0 @@ -# FillRaw - -პრობლემა: -``` -კარელი დგას 1x1 უჯრაზე, შეავსებინეთ მას პირველი ქუჩა ბრილიანტებით. ანუ პირველი ქუჩის ყველა უჯრაზე უნდა იდოს ზუსტად ერთი ბრილიანტი. ჩათვალეთ რომ საწყის სამყაროში ბრილიანტები არსად არ დევს. -``` - - - -## პრობლემის გადაჭრის გზა -პირველ რიგში ჩვენი პრობლემა დავყოთ ორ ნაწილად -* კარელმა უნდა გაიაროს მთლიანი ქუჩა -* სიარულთან ერთად, კარელმა პარალელურად უნდა განათავსოს ბრილიანტები ქუჩაზე - ---- - -### მთლიანი ქუჩის გავლის პრობლემას გადავჭრით შემდეგი საშაულებით -1. გამოვიყენოთ კარელისათვის ცნობილი `frontIsClear()` მეთოდი. -2. გამოვიყენოთ `while` ციკლი. -3. წინაღობის შემოწმების შემდგომ, განვახორციელოთ 1 უჯრით წინ გადანაცვლება `move()` მეთოდის საშუალებით, რათა შევძლოთ მთლიანი ქუჩის გავლა. - - -შესაბამისად, მივიღებთ კოდს: -```java -while(frontIsClear()) { - move(); -} -``` - -> *როდესაც კარელის წინ აღმოჩნდება კედელი, `frontIsClear()` მეთოდი `while` ციკლს შეატყობინებს, რომ კარელის წინ დაბრკოლებაა და კარელი შეწყეტს სვლას (ციკლი დასრულდება).* - ---- - -### სიარულის პარალელურად, ბრილიანტების განთავსება ქუჩაზე -ახლა, როდესაც კარელი წარმატებით გადის ქუჩას, მნიშვნელოვანია ბრილინატების განთავსება, რომელსაც `putBeeper()` მეთოდის საშუალებით გავაკეთებთ. მისი `while` ციკლში მოთავსებით კოდი მიიღებს შემდეგ სახეს -```java -while(frontIsClear()) { - putBeeper(); - move(); -} -``` -ამ ეტაპისთვის გვგონია, რომ პრობლემა გადაჭრილია და არანაირ მოდიფიკაციას არ საჭიროებს, თუმცა კოდის წარმატებით გაშვების შემდგომ მივიღებთ სამყაროს, სადაც კარელმა წარმატებით შეძლო მისი ქუჩის უჯრებზე მხოლოდ 1 ბრილიანტის განთავსება, **გარდა 1 უჯრისა.** - -ეს უჯრა, სწორედ ისაა, რომელზედაც იგი მუშაობის დასასრულს იმყოფება. მოდით, განვიხილოთ თუ რას შეიძლებოდა გამოეწვია მსგავსი სახის პრობლემა. -* ამ შეცდომას პროგრამირებაში [Off-By-One Error](https://stackoverflow.com/questions/2939869/what-is-an-off-by-one-error-and-how-do-i-fix-it)-ად მოიხსენიებენ და თავისი სახელიდან გამომდინარე მივხვდებით, რომ შეცდომა ძირითადად **იტერირების** დროს შეიძლება წარმოიშვას. -* განვიხილოთ კარელის შემთხვევა (სამყარო 10x10-ზე) - * კარელი დგას მე-9 უჯრაზე. - * დავდეთ ბრილიანტი. - * გამოვიძახეთ `move()` მეთოდი და გადავინაცვლეთ მე-10 უჯრაზე. - * მე-10 უჯრაზე გადასვლის შემდგომ კარელი ამოწმებს, აქვს თუ არა მას წინაღობა. იგი იძახებს`frontIsClear()` მეთოდს, რაზეც იგი იღებს პასუხს, რომ მის წინ მდებარეობს კედელი. შესაბამისად, კარელი ასრულებს `while` ციკლს და ვეღარ ახერხებს `putBeeper()` მეთოდის გამოძახებას. - -![Image Of Karel Standing on 10th Box](/problem-set/images/XQRF1oc.png) - -ამ ყველაფრის შემდგომ, პრობლემის გადასაჭრელად, დაგვჭირდება `putBeeper()` მეთოდის ჩამატება ჩვენს კოდში: -```java -while(frontIsClear()) { - putBeeper(); - move(); -} -// დებს ბრილიანტს, ბოლო უჯრაზე -putBeeper(); -``` ---- - -## რატომ იმუშავებს კოდი ნებისმიერი სამყაროს ზომისათვის? -* განვიხილოთ სამყარო, რომლის სიგრძეც 1-ის ტოლია (სიმაღლეს მნიშვნელობა არ აქვს, ვინაიდან კარელი მოძრაობს მარცხნიდან მარჯვნივ, მხოლოდ). - * ჩვენს მიერ დაწერილი კოდი, პირველ რიგში შეამოწმებს, არის თუ არა კარელის წინ კედელი. - * `frontIsClear()` მეთოდი დააბრუნებს პასუხს, რომ კარელს გზა დაბლოკილი აქვს და ვერ შეძლებს წინ წასვლას, შესაბამისად კარელი გამოტოვებს `while` ციკლს - * კარელი გადაინაცვლებს `while` ციკლის შემდგომ ბრძანებაზე და დახვდება `putBeeper()` მეთოდი, რაც უზრუნველყოფს ბრილიანტის წარმატებით დადებას სამყაროში. შესაბამისად, ჩვენი პრობლემაც გადაჭრილია და კარელი იდგება ქუჩაზე, რომლის ყველა უჯრაზე მხოლოდ 1 ბრილიანტი დევს - - ![Karel in 1x10 World](/problem-set/images/RwZB99V.png) - -* ნებისმიერი სხვა სამყაროსთვის, რომლის სიგრძეც > 1, ზუსტად იგივე ლოგიკით იმუშავებს, როგორც ზემოთხსენებული 10x10 სამყაროზე იმიტომ, რომ კარელი ამოწმებს წინ არსებულ დაბრკოლებას და მხოლოდ ამის შემდგომ იღებს გადაწყვეტილებას, გააგრძელოს თუ დაასრულოს სვლა. - ---- - -## შესაძლო ხარვეზები ამოხსნის იმპლემენტაციისას -პირველ რიგში, მინდა მოგილოცოთ პროგრამის წარმატებით დასრულება და იმპლემენტაცია, თუმცა არსებობს 2 დეტალი, რისი გათვალისწინებაც საკმაოდ მნიშვნელოვანია ამ ამოცანის გადაჭრისას - -1. გასათვალისწინებელია ზემოთ ნახსენები [Off-By-One Error](https://stackoverflow.com/questions/2939869/what-is-an-off-by-one-error-and-how-do-i-fix-it) -2. ასევე, საინტერესოა, რა მოხდება თუ `while` ციკლის ტანში ადგილებს გავუცვლით `move()`-სა და `putBeeper()` მეთოდებს. ერთი შეხედვით თითქოს ისევ სწორად უნდა იმუშაოს პროგრამამ, ვინაიდან ჩვენ მხოლოდ 2 ბრძანებას გავუცვალეთ ადგილი. - 1. მივიღებთ კოდს - ```java - while(frontIsClear()) { - move(); - putBeeper(); - } - putBeeper(); - ``` - 2. თუ კარგად დავაკვირდებით და გავიაზრებთ კოდს, შევამჩნევთ, რომ კარელის ქმედებები შეიცვლება. - * კოდის გაშვების შემდეგ კარელი ჯერ გადაინაცვლებს მეორე უჯრაზე, ხოლო ამის შემდეგ დადებს ბრილიანტს. - * თავისთავად ჩვენს კოდში წარმოიშვა ხარვეზი, რაც მოიაზრებს შემდეგს: კარელის სამყაროში პირველი უჯრა ყოველთვის ცარიელი დარჩება, ხოლო დამთავრების ადგილას იგივე განათავსებს 2 ბრილინატს. - - ![Karel in 1x10 World](/problem-set/images/karel_bug_0.png) - - 3. ამ პრობლემას 1 მარტივი გადაჭრის გზა აქვს. - 1. ავიტანოთ `while` ციკლის გარეთ მყოფი `putBeeper()` მეთოდი და გამოვიძახოთ `while` ციკლის გამოძახებამდე. - 2. ახალი კოდი - ```java - putBeeper(); - while(frontIsClear()) { - move(); - putBeeper(); - } - ``` diff --git a/problem-set/house.java b/problem-set/house.java new file mode 100644 index 0000000..0ddf567 --- /dev/null +++ b/problem-set/house.java @@ -0,0 +1,71 @@ +import java.awt.Color; + +import acm.graphics.GLine; +import acm.graphics.GOval; +import acm.graphics.GRect; +import acm.program.GraphicsProgram; + +public class house extends GraphicsProgram { + + private static final int WALL_WIDTH = 300; + private static final int WALL_HEIGHT = 300; + private static final int ROOF_HEIGHT = 100; + private static final int HANDLE_OFFSET = 15; + private static final int HANDLE_SIZE = 5; + private static final Color WALL_CLR = Color.GRAY; + private static final Color ROOF_CLR = Color.BLACK; + private static final Color WINDOW_CLR = Color.YELLOW; + private static final Color DOOR_CLR = Color.DARK_GRAY; + private static final Color HANDLE_CLR = Color.BLACK; + public void run() { + drawHouse(); + } + + private void drawHouse() { + drawWall(); + drawWindows(); + drawDoor(); + drawRoof(); + } + + private void drawWall() { + GRect wall = new GRect(getWidth()/2 - WALL_WIDTH/2, getHeight() - WALL_HEIGHT, WALL_WIDTH, WALL_HEIGHT); + wall.setFilled(true); + wall.setColor(WALL_CLR); + add(wall); + } + + private void drawWindows() { + GRect window1 = new GRect(getWidth() / 2 - 3*WALL_WIDTH/8, getHeight() - 2*WALL_HEIGHT/3, WALL_WIDTH/4, WALL_HEIGHT / 5); + window1.setFilled(true); + window1.setFillColor(WINDOW_CLR); + add(window1); + GRect window2 = new GRect(getWidth() / 2 + WALL_WIDTH/8, getHeight() - 2*WALL_HEIGHT/3, WALL_WIDTH/4, WALL_HEIGHT / 5); + window2.setFilled(true); + window2.setFillColor(WINDOW_CLR); + add(window2); + } + + private void drawDoor() { + GRect door = new GRect(getWidth() / 2 - WALL_WIDTH/8, getHeight() - WALL_HEIGHT/3, WALL_WIDTH/4, WALL_HEIGHT/3); + door.setFilled(true); + door.setColor(DOOR_CLR); + add(door); + GOval handle = new GOval(getWidth() / 2 - WALL_WIDTH/8 + HANDLE_OFFSET, getHeight() - WALL_HEIGHT/3 + 2*HANDLE_OFFSET, + HANDLE_SIZE, HANDLE_SIZE); + handle.setFilled(true); + handle.setColor(HANDLE_CLR); + add(handle); + } + + private void drawRoof() { + GLine line1 = new GLine(getWidth()/2 - WALL_WIDTH/2, getHeight() - WALL_HEIGHT, getWidth() / 2, + getHeight() - WALL_HEIGHT - ROOF_HEIGHT); + line1.setColor(ROOF_CLR); + add(line1); + GLine line2 = new GLine(getWidth() / 2, getHeight() - WALL_HEIGHT - ROOF_HEIGHT, + getWidth()/2 + WALL_WIDTH/2, getHeight() - WALL_HEIGHT); + line1.setColor(ROOF_CLR); + add(line2); + } +} \ No newline at end of file diff --git a/problem-set/houseExp.md b/problem-set/houseExp.md new file mode 100644 index 0000000..90e7ad8 --- /dev/null +++ b/problem-set/houseExp.md @@ -0,0 +1,88 @@ +# house + +## პრობლემა: +გრაფიკული პროგრამის გამოყენებით დახატეთ სახლი. + +## ამოხსნა: +პირველ რიგში ჩვენი პრობლემა დავყოთ რამდენიმე ნაწილად: +* დავხატოთ კონტური (კედლები) +* დავხატოთ ფანჯრები +* დავხატოთ კარი +* დავხატოთ სახურავი + +--- + +### მომზადება პრობლემის გადასაჭრელად +გრაფიკულ პრობლემებში მუდმივად გვჭირდება მუდმივი ცვლადები, რომლებიც საგნების ზომას(მაგალითად, + ჩვენს შემთხვევაში, სახლის სიმაღლე, სახურავის სიმაღლე, კედლების ფერი...) განსაზღვრავენ. +ამიტომ, სანამ უშუალოდ კოდის წერას დავიწყებთ, კარგი იქნება თუ ამ მუდმივ ცვლადებს ცალკე აღვწერთ +private static final ის დახმარებით. ეს საშუალებას მოგვცემს სურვილის შემთხვევაში მარტივად შევცვალოთ განზომილებები. + +### ნახატის სიმეტრიულობის პრობლემის გადაჭრის გზა +უკეთესია თუ ნებისმიერი ზომის კანვასზე სახლი სიმეტრიულად, შუაში დაიხატება. ამ პრობლემის +გადაჭრა შესაძლებელია ფუნქციით getWidth() და getHeight(). პირველი აბრუნებს კანვასის სიგანეს (x), +ხოლო მეორე კი მის სიმაღლეს(y). ამის საშუალებით ჩვენ შეგვიძლია ყოველთვის კანვასის შუაში დავხატოთ სახლის კონტური. +ამ შემთხვევაში მე გადავწვიტე, რომ სახლის სიგანე 8 ნაწილად დავყო, სიმაღლე კი 3-ად. შედეგად, ყოველთვის +სახლის ზომების პროპორციულად შემეძლება ობიექტების ჩამატება. + +> *GRect ობიექტს 4 ცვლადის გადაცემის შემთხვევაში ჯერ გადაეცემა x კოორდინატი, შემდეგ y, ხოლო ამის შემდეგ სიგანე და სიგრძე. 2 ცვლადის გადაცემის შემთხვევაში კი ჯერ სიგანე უნდა გადავცეთ, შემდეგ სიმაღლე და ობიექტი კანვასის სათავეში შეიქმნება.* + +> *გავითვალისწინოთ, რომ კანვასში სათავედ ითვლება ზედა მარცხენა კუთხე. x კოორდინატი ითვლება მარცხნიდან მარჯვნივ,ხოლო y ზემოდან ქვემოთ.* + +მაგალითად, კედლების კოორდინატების არჩევის შემთხვევაში მივიღებთ კოდს: +```java + GRect wall = new GRect(getWidth()/2 - WALL_WIDTH/2, getHeight() - WALL_HEIGHT, WALL_WIDTH, WALL_HEIGHT); +} +``` + +ხოლო ფანჯრის კოორდინატის არჩევის შემთხვევაში შემდეგს: +```java +GRect window1 = new GRect(getWidth() / 2 - 3*WALL_WIDTH/8, getHeight() - 2*WALL_HEIGHT/3, WALL_WIDTH/4, WALL_HEIGHT / 5); + GRect window2 = new GRect(getWidth() / 2 + WALL_WIDTH/8, getHeight() - 2*WALL_HEIGHT/3, WALL_WIDTH/4, WALL_HEIGHT / 5); +``` +> *პირველი ფანჯრის x კოორდინატად ვიღებ სახლის სიგანის პირველ მერვედს, ხოლო მეორე ფანჯრისთვის 5/8-ს. y კოორდინატად კი ორივესთვის სახლის სიმაღლის პირველი მესამედი ავარჩიე* + +--- + +### სახლის კომპონენტების დახატვა +როგორც ზემოთ აღვწერეთ, უკვე მარტივად შეგვიძლია კანვასის ზომების დამოუკიდებლად, სახლის კომპონენტებისთვის +კოორდინატების არჩევა. კარის x კოორდინატად გადავწვიტე ამეღო სახლის სიგრძის პირველი 3/8-დი. სიგანე იყოს +სახლის სიგანის მეოთხედი, სიმაღლე კი სახლის სიმაღლის მესამედი. +მივიღებთ კოდს: +```java + GRect door = new GRect(getWidth() / 2 - WALL_WIDTH/8, getHeight() - WALL_HEIGHT/3, WALL_WIDTH/4, WALL_HEIGHT/3); + ``` + + სახელურის კოორდინატები კარის კოორდინატებზე დამოკიდებული იყოს. მისი x კოორდინატი კარის ამავე კოორდინატისგან + დაშორებული იყოს `HANDLE_OFFSET` ზომით, რომელიც დასაწყისში უკვე აღვწერეთ. სახელურის ფორმა ოვალი იყოს და რადიუსიც + დავაფიქსიროთ `HANDLE_SIZE` ცვლადით. + + მივიღებთ კოდს: + ```java + GOval handle = new GOval(getWidth() / 2 - WALL_WIDTH/8 + HANDLE_OFFSET, getHeight() - WALL_HEIGHT/3 + 2*HANDLE_OFFSET,HANDLE_SIZE, HANDLE_SIZE); +``` + +სახურავის ფორმად სამკუთხედი ავარჩიე. GObjectს ასეთი შვილობილი კლასი არ ჰყავს, ამიტომ GLine-ის საშუალებით +დავხატოთ. GLineს საწყისი და საბოლოო კოორდინატები გადაეცემა, რომელიც სახლის კედლების კოორდინატებზე დამოკიდებული +იქნება. კერძოთ, პირველი ხაზის საწყისი კოორდინატი იქნება ზემოთ აღწერილი GRect-ის საწყისი კოორდინატი, ხოლო +საბოლოო კოორდინატი სიმეტრიულობის გამო კანვასის შუაში იქნება. სახურავის სიმაღლე მუდმივ ცვლადად(ROOF_HEIGHT) +აღვწეროთ. მეორე ხაზის საწყისი კოორდინატი კი პირველი ხაზის საბოლოო კოორდინატი იქნება. მისი საბოლოო Y +კოორდინატი GRect ობიექტისას დაემთხვევა, ხოლო X კი WALL_WIDTHით იქნება დაშორებული. + +მივიღებთ კოდს: +```java +GLine line1 = new GLine(getWidth()/2 - WALL_WIDTH/2, getHeight() - WALL_HEIGHT, getWidth() / 2, + getHeight() - WALL_HEIGHT - ROOF_HEIGHT); +GLine line2 = new GLine(getWidth() / 2, getHeight() - WALL_HEIGHT - ROOF_HEIGHT, + getWidth()/2 + WALL_WIDTH/2, getHeight() - WALL_HEIGHT); +``` + +> *არ დაგვავიწყდეს, რომ ობიექტები აღწერის შემდეგ add ფუნქციის საშუალებით უნდა დავამატოთ.* + +## შესაძლო ხარვეზები ამოხსნის იმპლემენტაციისას +ყველაზე მთავარი შეცდომა, რაც შეიძლება ამ ამოცანაში გამოგვეპაროს არის ფუნქციების შესრულების მიმდევრობა. მაგალითად, +თუ გადავწყვეტთ, რომ ჯერ დავხატოთ ფანჯრები და შემდეგ კედლები, რომელსაც გავაფერადებთ, ამ შემთხვევაში უნდა +გავითვალისწინოთ, რომ კედლები გადაფარავს ფანჯრებს და ვერ დავინახავთ. ამ პრობლემის გამოსასწორებლად 2 გზა არსებობს: +* შევცვალოთ ფუნქციების შესრულების მიმდევრობა: ჯერ კედლები დავხატოთ, შემდეგ ფანჯრები +* გამოვიყენოთ sendToFront() ან sendForward() ფუნქციები. პირველი დახმარებით ობიექტი პირველ დონეზე გადავა, ხოლო +მეორეს დახმარებით კი ერთი დონით წინ გადავა. From 26800dfd121d5604044249531fbb940f52e3ea49 Mon Sep 17 00:00:00 2001 From: zurabi Date: Mon, 31 Oct 2022 18:20:08 +0400 Subject: [PATCH 3/5] updated house.java and house.md, deleted extra files --- problem-set/average15.java | 16 ---- problem-set/average15Explanation.md | 45 ----------- problem-set/house.java | 119 +++++++++++++++------------- problem-set/houseExp.md | 1 - 4 files changed, 62 insertions(+), 119 deletions(-) delete mode 100644 problem-set/average15.java delete mode 100644 problem-set/average15Explanation.md diff --git a/problem-set/average15.java b/problem-set/average15.java deleted file mode 100644 index 8f20b83..0000000 --- a/problem-set/average15.java +++ /dev/null @@ -1,16 +0,0 @@ - -import acm.program.*; - -public class average15 extends ConsoleProgram { - - public void run() { - getAverage(); - } - - // reads 2 integers from console and outputs their average - private void getAverage() { - int a = readInt("Enter number #1: "); // read first integer - int b = readInt("Enter number #2: "); // read second integer - println((double) (a + b) / 2); // output average - } -} diff --git a/problem-set/average15Explanation.md b/problem-set/average15Explanation.md deleted file mode 100644 index f80feb8..0000000 --- a/problem-set/average15Explanation.md +++ /dev/null @@ -1,45 +0,0 @@ -# 15.average - -ამოცანა: -კონსოლიდან წაიკითხეთ ორი მთელი რიცხვი და დაბეჭდეთ მათი საშუალო არითმეტიკული - -##ამოხნის გზა -* ნაწილი 1: ვიღებთ მომხმარებლისგან ინფორმაციას -* ნაწილი 2: ვამუშავებთ ამ ინფორმაციას დავალების მიხედვით, ვკრებთ და შუაზე ვყოფთ. - ---- -მომხმარებლისგან მთელი რიცხვის წაკითხვა შესაძლებელია `readInt()` ფუნქციის საშუალებით. -შესაძლებელია მომხმარებელს მესიჯიც გამოვუტანოთ, თუ რა ტიპის ინფორმაცია უნდა შეიყვანოს. ---- -მომხმარებლის მიერ ინფორმაციის შემოსვლის შემდეგ დავამუშავოთ რიცხვები, შევკრიბოთ და შუაზე გავყოთ. -შედარებით ბევრი ოპერაცია რომ იყოს გასაკეთებლი, უმჯობესი იქნებოდა თუ ცალკე მეთოდად გავიტანდით, -თუმცა ამ შემთხვევაში ამის საჭიროება არ არის. ---- - -## რა ტიპის შეცდომები შეიძლება დავუშვათ იმპლემენტაციისას -1. -მთავარი შეცდომა რაც შეიძლება ამ ამოცანაში დავუშვათ, არის პირდაპირ შემოსული ორი რიცხვის შეკრება და შუაზე გაყოფა. -ერთი შეხედვით თითქოს ამან სწორად უნდა იმუშავოს, მაგრამ არ ვითვალისწინებთ, რომ მომხმარებლის მიერ შემოსული ინფორმაცია -მთელ რიცხვებშია (`int` ტიპისაა) ჩაწერილი. თუ ორი რიცხვიდან არცერთია მეორეს ჯერადი და ჩვენ მათ ერთმანეთზე გაყოფას შევეცდებით, -პროგრამა ავტომატურად ქვემოთ დაამრგვალებს მიღებულ შედეგს. მაგალითად: თუ 5ს გავყოფთ 2-ზე მივიღებთ 2.5-ის ქვემოთ -დამრგვალებულ რიცხვს, ანუ 2, რაც ჩვენთვის არასასურველ პასუხს მოგვცემს. -ამ პრობლემის გადაჭრა cast-ის საშუალებით შეგვიძლია (cast არის ტერმინი და არა მეთოდი). cast-ის საშუალებით შეგვიძლია -კომპილატორს "ვუთხრათ", რომ კონკრეტული ცვლადი განიხილოს ისეთ ტიპად, როგორიც გვჭირდება. მაგალითად , -თუ `int` ტიპის ცვლად a - ს cast-ის საშუალებით `double` ტიპს მივანიჭებთ (`double(a)`), -ის განიხილება როგორც ათწილადი და არა მთელი რიცხვი. - -2. -კიდევ ერთი შეცდომა რაც შეიძლება ამ დავალებაში დავუშვათ არის შემდეგი: `println(double((a+b)/2))`. თითქოს ზემოთ აღწერილი -შეცდომა გავითვალისწინეთ და cast გამოვიყენეთ ათწილადში გადასაყვანად, თუმცა პასუხს არასწორს მივიღებთ. ამის მიზეზი კი არის ის, -თუ რა მიმდევრობით ხდება ოპერაციების გაკეთება. განვხილოთ შემთხვევა a = 5, b = 2: პირველ რიგში შეიკრიბება a და b. -მიღებული პასუხი, 7, `int` ტიპის იქნება. შემდეგ გაიყოფა ორზე, და რადგან ორივე რიცხვი `int` ტიპის იყო, ქვემოთ დამრგვალდება, -მივიღებთ 3-ს. მხოლოდ ამის შემდეგ მოხდება cast და 3 განიხილება როგორც ათწილადი (`double`), დაიბეჭდება პასუხი 3.0. - -## სწორი ამონახსნი -სწორ ამონახსნს მხოლოდ იმ შემთხვევაში მივიღებთ, თუ ამ ორი რიცხვიდან ერთ-ერთს ან მათ ჯამს განვიხილავთ ათწილადად. -* გზა 1. cast გამოვიყენოთ a-ზე; -* გზა 2. cast გამოვიყენოთ b-ზე; -* გზა 3. cast გამოვიყენოთ (a+b)-ზე; - -მთავარია რიცხვი განვიხილოთ double ტიპად, სანამ მას 2-ზე გავყოფთ. პასუხი გამოვიტანოთ `println()` -ფუნქციით. \ No newline at end of file diff --git a/problem-set/house.java b/problem-set/house.java index 0ddf567..309ff95 100644 --- a/problem-set/house.java +++ b/problem-set/house.java @@ -7,65 +7,70 @@ public class house extends GraphicsProgram { - private static final int WALL_WIDTH = 300; - private static final int WALL_HEIGHT = 300; - private static final int ROOF_HEIGHT = 100; - private static final int HANDLE_OFFSET = 15; - private static final int HANDLE_SIZE = 5; - private static final Color WALL_CLR = Color.GRAY; - private static final Color ROOF_CLR = Color.BLACK; - private static final Color WINDOW_CLR = Color.YELLOW; - private static final Color DOOR_CLR = Color.DARK_GRAY; - private static final Color HANDLE_CLR = Color.BLACK; - public void run() { - drawHouse(); - } + private static final int WALL_WIDTH = 300; + private static final int WALL_HEIGHT = 300; + private static final int ROOF_HEIGHT = 100; + private static final int HANDLE_OFFSET = 15; + private static final int HANDLE_SIZE = 5; + private static final Color WALL_CLR = Color.GRAY; + private static final Color ROOF_CLR = Color.BLACK; + private static final Color WINDOW_CLR = Color.YELLOW; + private static final Color DOOR_CLR = Color.DARK_GRAY; + private static final Color HANDLE_CLR = Color.BLACK; - private void drawHouse() { - drawWall(); - drawWindows(); - drawDoor(); - drawRoof(); - } + public void run() { + drawHouse(); + } - private void drawWall() { - GRect wall = new GRect(getWidth()/2 - WALL_WIDTH/2, getHeight() - WALL_HEIGHT, WALL_WIDTH, WALL_HEIGHT); - wall.setFilled(true); - wall.setColor(WALL_CLR); - add(wall); - } + private void drawHouse() { + drawWall(); + drawWindows(); + drawDoor(); + drawRoof(); + } - private void drawWindows() { - GRect window1 = new GRect(getWidth() / 2 - 3*WALL_WIDTH/8, getHeight() - 2*WALL_HEIGHT/3, WALL_WIDTH/4, WALL_HEIGHT / 5); - window1.setFilled(true); - window1.setFillColor(WINDOW_CLR); - add(window1); - GRect window2 = new GRect(getWidth() / 2 + WALL_WIDTH/8, getHeight() - 2*WALL_HEIGHT/3, WALL_WIDTH/4, WALL_HEIGHT / 5); - window2.setFilled(true); - window2.setFillColor(WINDOW_CLR); - add(window2); - } + private void drawWall() { + GRect wall = new GRect(getWidth() / 2 - WALL_WIDTH / 2, getHeight() - WALL_HEIGHT, WALL_WIDTH, WALL_HEIGHT); + wall.setFilled(true); + wall.setColor(WALL_CLR); + add(wall); + } + + private void drawWindows() { + GRect window1 = new GRect(getWidth() / 2 - 3 * WALL_WIDTH / 8, getHeight() - 2 * WALL_HEIGHT / 3, + WALL_WIDTH / 4, WALL_HEIGHT / 5); + window1.setFilled(true); + window1.setFillColor(WINDOW_CLR); + add(window1); + GRect window2 = new GRect(getWidth() / 2 + WALL_WIDTH / 8, getHeight() - 2 * WALL_HEIGHT / 3, WALL_WIDTH / 4, + WALL_HEIGHT / 5); + window2.setFilled(true); + window2.setFillColor(WINDOW_CLR); + add(window2); + } + + private void drawDoor() { + GRect door = new GRect(getWidth() / 2 - WALL_WIDTH / 8, getHeight() - WALL_HEIGHT / 3, WALL_WIDTH / 4, + WALL_HEIGHT / 3); + door.setFilled(true); + door.setColor(DOOR_CLR); + add(door); + GOval handle = new GOval(getWidth() / 2 - WALL_WIDTH / 8 + HANDLE_OFFSET, + getHeight() - WALL_HEIGHT / 3 + 2 * HANDLE_OFFSET, HANDLE_SIZE, HANDLE_SIZE); + handle.setFilled(true); + handle.setColor(HANDLE_CLR); + add(handle); + } + + private void drawRoof() { + GLine line1 = new GLine(getWidth() / 2 - WALL_WIDTH / 2, getHeight() - WALL_HEIGHT, getWidth() / 2, + getHeight() - WALL_HEIGHT - ROOF_HEIGHT); + line1.setColor(ROOF_CLR); + add(line1); + GLine line2 = new GLine(getWidth() / 2, getHeight() - WALL_HEIGHT - ROOF_HEIGHT, + getWidth() / 2 + WALL_WIDTH / 2, getHeight() - WALL_HEIGHT); + line1.setColor(ROOF_CLR); + add(line2); + } - private void drawDoor() { - GRect door = new GRect(getWidth() / 2 - WALL_WIDTH/8, getHeight() - WALL_HEIGHT/3, WALL_WIDTH/4, WALL_HEIGHT/3); - door.setFilled(true); - door.setColor(DOOR_CLR); - add(door); - GOval handle = new GOval(getWidth() / 2 - WALL_WIDTH/8 + HANDLE_OFFSET, getHeight() - WALL_HEIGHT/3 + 2*HANDLE_OFFSET, - HANDLE_SIZE, HANDLE_SIZE); - handle.setFilled(true); - handle.setColor(HANDLE_CLR); - add(handle); - } - - private void drawRoof() { - GLine line1 = new GLine(getWidth()/2 - WALL_WIDTH/2, getHeight() - WALL_HEIGHT, getWidth() / 2, - getHeight() - WALL_HEIGHT - ROOF_HEIGHT); - line1.setColor(ROOF_CLR); - add(line1); - GLine line2 = new GLine(getWidth() / 2, getHeight() - WALL_HEIGHT - ROOF_HEIGHT, - getWidth()/2 + WALL_WIDTH/2, getHeight() - WALL_HEIGHT); - line1.setColor(ROOF_CLR); - add(line2); - } } \ No newline at end of file diff --git a/problem-set/houseExp.md b/problem-set/houseExp.md index 90e7ad8..ac8e698 100644 --- a/problem-set/houseExp.md +++ b/problem-set/houseExp.md @@ -32,7 +32,6 @@ private static final ის დახმარებით. ეს საშუ მაგალითად, კედლების კოორდინატების არჩევის შემთხვევაში მივიღებთ კოდს: ```java GRect wall = new GRect(getWidth()/2 - WALL_WIDTH/2, getHeight() - WALL_HEIGHT, WALL_WIDTH, WALL_HEIGHT); -} ``` ხოლო ფანჯრის კოორდინატის არჩევის შემთხვევაში შემდეგს: From 3d7c2d9746e86e77cc7322deca3ef29a8fb1ff50 Mon Sep 17 00:00:00 2001 From: zurabi Date: Mon, 31 Oct 2022 19:33:30 +0400 Subject: [PATCH 4/5] implemented TossesFor3Heads problem --- problem-set/tossesFor3Heads.java | 38 ++++++++++++++++++ problem-set/tossesFor3HeadsExp.md | 65 +++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 problem-set/tossesFor3Heads.java create mode 100644 problem-set/tossesFor3HeadsExp.md diff --git a/problem-set/tossesFor3Heads.java b/problem-set/tossesFor3Heads.java new file mode 100644 index 0000000..535a23f --- /dev/null +++ b/problem-set/tossesFor3Heads.java @@ -0,0 +1,38 @@ +import acm.program.*; +import acm.util.RandomGenerator; + +public class tossesFor3Heads extends ConsoleProgram { + + private RandomGenerator rand = RandomGenerator.getInstance(); + private static final int numExperiments = 100000; + private static final int numHeads = 3; + + public void run() { + println(avrgTosses(numHeads, numExperiments)); + } + + private double avrgTosses(int numHeads, int numExperiments) { + int average = 0; + for (int i = 0; i < numExperiments; i++) { + average += simulation(numHeads); + } + + return (double) average / numExperiments; + } + + private int simulation(int numHeads) { + int heads = 0; + int flips = 0; + while (heads != numHeads) { + boolean flip = rand.nextBoolean(); + if (flip) { + heads++; + } else { + heads = 0; + } + flips++; + } + + return flips; + } +} \ No newline at end of file diff --git a/problem-set/tossesFor3HeadsExp.md b/problem-set/tossesFor3HeadsExp.md new file mode 100644 index 0000000..cb6c54b --- /dev/null +++ b/problem-set/tossesFor3HeadsExp.md @@ -0,0 +1,65 @@ +# tossesFor3heads + +## პრობლემა: +თქვენი ამოცანაა გააკეთოთ მონეტის აგდების სიმულაციები და დათვალოთ საშუალოდ +რამდენჯერ უნდა ავაგდოთ მონეტა რათა 3-ჯერ ზედიზედ ამოვიდეს ბორჯღალო. + +## ამოხსნა: +პირველ რიგში ჩვენი პრობლემა დავყოთ რამდენიმე ნაწილად: +* დავითვალოთ ერთი ექსპერიმენტის დროს, რამდენ აგდებაში მოვა ბორჯღალო. +* ასეთი ექსპერიმენტი ჩავატაროთ მუდმივა `numExperiments` რაოდენობაჯერ. +* ავჯამოთ მიღებული შედეგები და გავყოთ ექსპერიმენტების რაოდენობაზე. + +--- + +### მომზადება პრობლემის გადასაჭრელად +უმჯობესი იქნება თუ ექსპერიმენტების რაოდენობასა და ზედიზედ მოსული ბორჯღალოების რაოდენობას `private static final` +ცვლადებად გავიტანთ. შედეგად, მარტივად შეგვეძლება კოდში სურვილის მიხედვით ცვლილებების შეტანა. + +--- + +### დეკომპოზიციური სურათის აგება +`for` ციკლით, შეგვიძლია ექსპერიმენტების რაოდენობაჯერ ავჯამოთ `simulation` ფუნქციის მიერ დაბრუნებული მნიშვნელობა, რომელიც, როგორც ზემოთ აღვწერე, ერთ ექსპერიმენტს ჩაატარებს და დააბრუნებს, რამდენ აგდებაში მოვიდა `numheads` ბორჯღალო ზედიზედ. ამის შემდეგ გავყოთ მიღებული ჯამი ექსპერიმენტების რაოდენობაზე. მივიღებთ კოდს: + +```java + private double avrgTosses(int numHeads, int numExperiments) { + int average = 0; + for (int i = 0; i < numExperiments; i++) { + average += simulation(numHeads); + } + + return (double) average / numExperiments; + } +``` + +--- + +### `simulation` ფუნქციის იმპლემენტაცია +პირველ რიგში დავფიქრდეთ, რა ცვლადები დაგვჭირდება. აუცილებლად დაგვჭირდება შევინახოთ მონეტის აგდებების რაოდენობა. დავარქვათ მას `flips`. როგორმე უნდა დავიმახსოვროთ, თუ რამდენჯერ მოვიდა ზედიზედ ბორჯღალო. ამისთვის მეორე ცვლადი დაგვჭირდება, რომელსაც `heads` დავარქმევ. ჩვენ მანამდე უნდა "ავაგდოთ" მონეტა სანამ `heads` ცვლადი 3-ის ტოლი არ გახდება. ამისთვის გამოვიყენოთ `while` ციკლი. მონეტის აგდების სიმულაციის ერთ-ერთი და ალბათ ყველაზე მარტივი გზაა `RandomGenerator`-ის ფუნქციის `nextBoolean()`-ის საშუალებით შემთხვევითად დავაგენერიროთ `boolean`. დაგვიბრუნებს true-ს ან false-ს თანაბარი ალბათობით. პირობითად შეგვიძლია true ავიღოთ ბორჯღალოს მოსვლად, ხოლო false - არიოლად. +რამდენჯერად ბორჯღალო მოვა გავზარდოთ `heads` ცვლადი. არ დაგვავიწყდეს, რომ თუ არიოლი მოვიდა `heads` უნდა გავანულოთ, ანუ ექსპერიმენტი ფაქტობრივად თავიდან იწყება. მივიღებთ კოდს: + +```java +private int simulation(int numHeads) { + int heads = 0; + int flips = 0; + while (heads != numHeads) { + boolean flip = rand.nextBoolean(); + if (flip) { + heads++; + } else { + heads = 0; + } + flips++; + } + + return flips; + } +``` + +## შესაძლო ხარვეზები ამოხსნის იმპლემენტაციისას +> მთავარი ხარვეზი, რაც შეიძლება დავუშვათ ამოხსნისას, არის არიოლის მოსვლისას ექსპერიმენტის ჩვეულებრივად გაგრძელება `heads` ცვლადის განულების გარეშე. მივიღებთ არასწორ შედეგს. + + +## ალტერნატიული ამოხსნა +> ამოხსნაში შეგვიძლია მონეტის აგდების სიმულაცია მოვახდინოთ სხვანაირად. `boolean`-ის დაგენერირების ნაცვლად შეგვიძლია +სიმეტრიულ ინტერვალში დავაგენერიროთ რიცხვი და თუ პირველ ნახევარში ჩავარდა პირობითად ბორჯღალოდ ავიღოთ, თუ მეორეში -არიოლად. ასევე `flips` ცვლადის ნაცვლად შეგვიძლია 3 `boolean` ცვლადი გვქონდეს, თუმცა ასეთ შემთხვევაში კოდის შეცვლის სურვილი თუ გვექნება(ვთქვათ, გადავწყვიტეთ დავითვალოდ საშუალოდ 4 ბორჯღალო რამდენ აგდებაში მოდის) უფრო გაგვიჭირდება. From d49b5f412c2243cf40994a07eba9812dfe3aa3a8 Mon Sep 17 00:00:00 2001 From: zurabi Date: Sun, 13 Nov 2022 18:02:40 +0400 Subject: [PATCH 5/5] implementation and explanation of 38.loseEverything --- problem-set/loseEverything.java | 39 +++++++++++++++++ problem-set/loseEverythingExp.md | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 problem-set/loseEverything.java create mode 100644 problem-set/loseEverythingExp.md diff --git a/problem-set/loseEverything.java b/problem-set/loseEverything.java new file mode 100644 index 0000000..bbdf938 --- /dev/null +++ b/problem-set/loseEverything.java @@ -0,0 +1,39 @@ +import acm.program.*; +import acm.util.RandomGenerator; + +public class loseEverything extends ConsoleProgram { + + private RandomGenerator rgen = RandomGenerator.getInstance(); + private static final int balance = 1000; + + int askUser(String message, int lowerBound, int upperBound) { + int num = readInt(message); + while (num < lowerBound || num > upperBound) { + num = readInt("Wrong input, try again: "); + } + return num; + } + + private void simulateGame(int deposit) { + while (deposit > 0) { + int bet = askUser("Place your bet: ", 0, deposit); + int guess = askUser("Choose number between 0 and 36: ", 0, 36); + int num = rgen.nextInt(0, 36); + if (guess == num) { + deposit *= 2; + println("You Win! current balance: " + deposit); + } else { + deposit -= bet; + println("You lose. current balance: " + deposit); + } + } + + println("Game Over, Good Game"); + } + + public void run() { + int deposit = balance; + simulateGame(deposit); + } + +} \ No newline at end of file diff --git a/problem-set/loseEverythingExp.md b/problem-set/loseEverythingExp.md new file mode 100644 index 0000000..f02fdea --- /dev/null +++ b/problem-set/loseEverythingExp.md @@ -0,0 +1,75 @@ +# loseEverything + +## პრობლემა: +მოხმარებელს აქვს საწყისი თანხა 1000, თამაშობს მანამ სანამ არ წააგებს ყველაფერს. ყოველ +ჯერზე შეყავს ფსონის თანხა და რიცხვი რომელზეც დებს. ხდება რულეტკის დატრიალების +სიმულაცია(0-დან 36-მდე შემთხვევითი რიცხვი), მოგების შემთხვევაში მოთამაშეს +უორმაგედბა დადებული თანხა წაგების შემთხვევაში კი მოთამაშე კარგავს დადებულ თანხას. +მოთამაშე მოგებულია მაშინ თუ რულეტკაზე ამოსული რიცხვი ემთხვევა მის მიერ დადებულ +რიცხვს. პროგრამა ყოველ მოქმედებაზე უნდა ბეჭდავდეს შესაბამის შეტყობინებას, ადვილად +რომ გაერკვეს მოთამაშე, რა რიცხვი ამოვიდა, რამდენი წააგო, რამდენი მოიგო, რამდენი აქვს +ბალანსი. + +## ამოხსნა: +პირველ რიგში ჩვენი პრობლემა დავყოთ რამდენიმე ნაწილად: +* საწყისი ბალანსის ინიციალიზაცია +* თამაშის სიმულაცია (0 დან 36-დე რიცხვის არჩევა) +* მომხმარებლისთვის შეტყობინების გამოტანა + +--- + +### მომზადება პრობლემის გადასაჭრელად +კარგი იქნება, თუ საწყის თანხას მუდმივად(private static final int) გავიტანთ, რათა სურვილის შემთხვევაში მარტივად შევძლოთ შეცვლა. მოვიფიქროთ, თუ როგორ მოვახდენთ რულეტკის სიმულაციას. ამოხსნის გზის მოფიქრების შემდეგ ავაგოთ შესაბამისი დეკომპოზიციური სურათი. + +--- + +### ამოხსნა + +* ჯერ ვიფიქროთ რულეტკის დატრიალების სიმულაციაზე. + ვთვლით, რომ რულეტკა 0-დან 36-მდე თითოეულ რიცხვზე თანაბარი ალბათობით მოხვდება. პროგრამაში ეს შეგვიძლია randomGenerator-ის დახმარებით გავაკეთოთ. მის `readInt()` ფუნქციას თუ გადავცემთ 2 არგუმენტს, 0-სა და 36-ს, თანაბარი ალბათობით დაგვიბრუნებს ამ ინტერვალში რომელიმე რიცხვს. ერთი დატრიალების ფსევდო კოდი ასე გამოიყურება: + +--- + დავაგენერიროთ რიცხვი `num` 0-დან 36მდე; + თუ მომხმარებლის შემოყვანილი რიცხვი დაემთხვა `num`-ს + გავზარდოთ მომხმარებლის ბალანსი + წინააღმდეგ შემთხვევაში + შევამციროთ მის მიერ დადებული თანხის მიხედვით. +--- + +* მომხმარებლის მიერ ფსონი დადება +პირველ რიგში, მომხმარებელი დებს თანხას, შემდეგ კი ცდილობს გამოიცნოს რულეტკაზე რა რიცხვი მოვა. ორივეს იმპლემენტაცია შესაძლებელია `readInt()` ფუნქციით. უნდა გავითვალისწინოთ, რომ მომხმარებელი არასწორი რიცხვი არ შემოიყვანოს. მაგალითად, დარჩენილ ბალანსზე მეტი თანხა არ დადოს, ან არასწორი რიცხვის გამოცნობა არ სცადოს. ანუ, უნდა შევამოწმოთ მომხმარებლის მიერ შემოყვანილი რიცხვი, თუ რაიმე საზღვრებს არ სცდება. შესაბამისად, კარგი იქნება, თუ დავწერთ მეთოდს, რომელიც შემოაყვანინებს მომხმარებელს რიცხვს, გამოიტანს შესაბამის მესიჯს და შეამოწმებს ამ რიცხვს საზღვრებზე. მომხმარებელს იქამდე უნდა ვთხოვოთ რიცხვის შემოყვანა, სანამ სწორად არ მოახერხებს ამას. ამისთვის `while` ციკლი გამოგვადგება. მივიღებთ კოდს: + +```java + int askUser(String message, int lowerBound, int upperBound) { + int num = readInt(message); + while (num < lowerBound || num > upperBound) { + num = readInt("Wrong input, try again: "); + } + return num; + } +``` + +არ დაგვავიწყდეს, რომ მომხმარებელი მარტივად უნდა გაერკვეს რამდენი თანხა დარჩა, რამდენი წააგო და ა.შ. შესაბამისად, ყოველი მოგებული ან წაგებული თამაშის შემდეგ უნდა გამოვიტანოთ მისი ბალანსი. ეს ყველაფერი იქამდე უნდა გავაგრძელოთ, სანამ თანხა 0-ს არ გაუტოლდება. მივიღებთ კოდს: + +```java + while (deposit > 0) { + int bet = askUser("Place your bet: ", 0, deposit); + int guess = askUser("Choose number between 0 and 36: ", 0, 36); + int num = rgen.nextInt(0, 36); + if (guess == num) { + deposit *= 2; + println("You Win! current balance: " + deposit); + } else { + deposit -= bet; + println("You lose. current balance: " + deposit); + } + } + + println("Game Over, Good Game"); +``` + +## შესაძლო ხარვეზები ამოხსნის იმპლემენტაციისას +მთავარი შეცდომა, რაც შეიძლება დავუშვათ, არის მომხმარებლის "ნდობა" და მისი შემოყვანილი რიცხვის არ-შემოწმება. არასწორმა რიცხვმა შეიძლება სრულიად არიოს პროგრამა. ამიტომ არის გამოსადეგი `askUser` მეთოდი. + +## ამოხსნის ალტერნატიული გზა +შეგვეძლო 1 მთლიანი დატრიალების სიმულაციის ნაცვლად შეგვექმნა მეთოდი, რომელიც მთელ რიცხვს დააბრუნებდა მოთამაშის არჩეული რიცხვის მიხედვით. მაგალითად, თუ მოიგებდა დავაბრუნებდით რა თანხა მოიგო, თუ წააგებდა - რა წააგო. სანამ ბალანსი 0-ზე მეტი იქნებოდა, გამოვაკლებდით ამ ფუნქციის მიერ დაბრუნებულ რიცხვს.