بازی حدس عدد


در این مسئله، سلیب می‌خواهد بازی‌ای تعریف کند تا از تنهایی ابدی خود نجات یابد و کمی سرگرم شود. این بار می‌خواهد تا یک برنامه تعاملی بنویسد تا عددی که کد دیگر او انتخاب کرده را پیدا کند :‌). بازی به این صورت است که سلیب می‌خواهد تابع GuessMyNumberرا در فایل main.go پیاده‌سازی کند. امضای این تابع به شکل زیر است:

extensionFromNamemain.go
func GuessMyNumber(game Game) string {
    ...
}
Go

این تابع یک شی از نوع Game را دریافت می‌کند. هدف این تابع پیدا کردن عددی است که بازی انتخاب کرده و در داخل Game ذخیره شده است. اما پیدا کردن به شکل مستقیم امکان ندارد و باید حتما بازی کنیم! در واقع ما فقط به یک تابع دسترسی داریم که حدس‌های خود را به آن می‌دهیم و حدسمان را بررسی می‌کند و به ما راهنمایی می‌کند که به جواب اصلی نزدیک‌تر شویم.

در نهایت تابع GuessMyNumber پس از اینکه حدس‌های مختلفی می‌زند و نهایتا پاسخ را پیدا می‌کند، پاسخ خود را با یک پیام به شکل رشته باز می‌گرداند:

Your Number was <NUMBER>
Plain text

برای مثال اگر عدد بازی برابر با 269باشد، این تابع پس از فهمیدن عدد باید عبارت زیر را برگرداند.

Your Number was 269
Plain text

جرئیات Game🔗

در فایل utils.go، اینترفیس Game آمده است که در اینجا نیز می‌توانیم آن را ببینیم:

extensionFromNameutils.go
package main

type Game interface {
    CheckNumber(n int) string
}
Go

تابع CheckNumber عددی که به عنوان حدس می‌گیرد را بررسی می‌کند و ۳ حالت مختلف را برمی‌گرداند:

  • اگر عدد ورودی تابع یا همان n از عدد مورد نظر بازی کمتر باشد، عبارت My Number is Greater را برمی‌گرداند.
    • اگر عدد ورودی تابع یا همان n از عدد مورد نظر بازی بیشتر باشد، عبارت My Number is Lower را برمی‌گرداند.
    • اگر عدد ورودی تابع یا همان n دقیقا برابر با عدد مورد نظر بازی باشد، عبارت CORRECT را برمی‌گرداند.

نمره‌دهی🔗

از آن‌جا که اجرای تابع CheckNumber برای سلیب هزینه دارد (و شاید هم به دنبال پیچاندن و سخت‌تر کردن مسئله است!) سلیب مسئله را در ۴ بخش تعریف کرده است:

مسئله نمره
با پرسیدن حداکثر ۳۶۰ پرسش به عدد برسد ۲۵
با پرسیدن حداکثر ۱۸۰ پرسش به عدد برسد ۲۵
با پرسیدن حداکثر ۶۰ پرسش به عدد برسد ۲۵
با پرسیدن حداکثر ۱۰ پرسش به عدد برسد ۲۵

هرچه الگوریتم شما برای یافتن عدد بهتر باشد، نمره بیشتری در این مسئله دریافت می‌کنید :‌) پس به دنبال راه‌حل‌های خلاقانه باشید.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به‌صورت زیر است:

.
├── go.mod
├── go.sum
├── main.go
├── utils.go
└── main_sample_test.go
Plain text

فایل utils.go شامل اینترفیس Game است. فایل main_sample_test.go نیز شامل یک پیاده‌سازی نمونه از Game است که برای تست‌های اولیه‌ی کد خود می‌توانید از آن استفاده کنید. فایل main.go نیز شامل تابعی‌ است که شما باید پیاده‌سازی کنید.

توجه: در این سؤال تنها مجاز به استفاده از کتاب‌خانه‌های bufio و fmt و math/rand و os و strings هستید.

محدوده‌ی عددی که باید حدس بزنید بین ۱ تا ۳۶۰ است🔗

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی تابع GuessMyNumber، فایل main.go را آپلود کنید.

QCalendar


بعد از تقویم‌های جلالی، میلادی و کلندرهای مجازی که گوگل و سایر شرکت‌ها ارائه می‌دهند؛ ما در کوئرا به فکر طراحی تقویم خودمان افتاده‌ایم. و وظیفه‌ی آن به اون یکی همکارمون در شرکت محول شده. از شما می‌خواهیم این پروژه را طبق معمول کوئرا برای اون یکی همکارمون پیاده‌سازی کنید.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. در این پروژه شما باید قسمت‌های مشخص‌شده با // TODO را پیاده‌سازی کنید.

جداول🔗

ساختار structهایی که جداول شما رو میسازند باید به شکل زیر باشد:

1- User

نام ستون نوع ملاحظات
gorm.Model struct PRIMARY KEY AUTO_INCREMENT
Username string نام کاربری
FirstName string نام
LastName string نام خانوادگی

2- Calendar

نام ستون نوع ملاحظات
gorm.Model struct PRIMARY KEY AUTO_INCREMENT
Name string نام تقویم
UserID uint کلید خارجی به جدول users و ستون id

3- Appointment

نام ستون نوع ملاحظات
gorm.Model struct PRIMARY KEY AUTO_INCREMENT
Subject string موضوع ملاقات
Description string توضیحات ملاقات
StartTime time.Time زمان شروع ملاقات
Length uint مدت زمان ملاقات
OwnerID uint آیدی دارنده ملاقات
OwnerType string تایپ کلاس دارنده ملاقات

4- TaskList

نام ستون نوع ملاحظات
gorm.Model struct PRIMARY KEY AUTO_INCREMENT

جهت اطلاعات بیشتر در مورد gorm.Model می‌توانید به این لینک مراجعه نمایید.

روابط🔗

در ادامه شما باید به پیاده‌سازی روابط بین مدل‌ها بپردازید که ارتباطات بین مدل‌ها باید به‌صورت زیر باشد:

  • هر کاربر یک تقویم دارد و هر تقویم متعلق به یک کاربر است.
  • هر کاربر می‌تواند چند وقت‌ملاقات داشته باشد و هر وقت‌ملاقات هم می‌تواند چند کاربر داشته باشد.(از طریق جدول appointment_user)
  • هر تقویم می‌تواند چند وقت‌ملاقات داشته باشد.
  • هر فهرست‌وظیفه می‌تواند چند وقت‌ملاقات داشته باشد.
  • هر وقت‌ملاقات متعلق به یک تقویم یا فهرست‌وظیفه است.

توابع🔗

شما باید بدنه توابع زیر را به شکلی مطلوب تکمیل نمایید.

تابع RefreshDatabase🔗

این تابع با دریافت آبجکتی از نوع *gorm.DB و لیستی از جداول، ابتدا باید آن‌ها را حذف کند و سپس آن‌ها را طبق آخرین تغییرات بر اساس استراکت‌های مربوطه بسازد. در صورت موفقیت‌آمیز بودن عملیات باید Refresh database successfully done به عنوان رشته برگردانده شود به همره nil و اگر خطایی در طی فرآیند رخ داد باید رشته خالی به همراه ارور مربوطه برگردانده شود. امضای تابع به شکل زیر است:

func RefreshDatabase(db *gorm.DB, tables []interface{}) (string, error){
    // TODO: Implement
}
Go

تابع SeedUser🔗

این تابع باید به صورت Seeder عمل کند و با دریافت پارامترهای لازم یک کاربر با تقویم و لیست ملاقات‌های مربوطه بسازد و در جدول ذخیره کند و در صورت موفقیت‌آمیز بودن عملیات پیام Seeding database successfully done را چاپ کند و در غیر این صورت ارور را به همراه یک رشته خالی برگرداند. امضای تابع به شکل زیر است:

func SeedUser(db *gorm.DB, username, firstName, lastName, calendarName, appointmentSubject string, startDate time.Time) (string, error) {
    // TODO: Implement
}
Go

تابع updateAppointment🔗

این تابع باید به تمام وقت‌ملاقات‌هایی که اسم تقویم آن‌ها برابر با calendarName، زمان شروع آنها بین startTime و endTime و موضوع رویداد، شامل keyword باشد یک ساعت به زمان شروع ملاقات‌ها اضافه کند و به انتهای توضیحات آن‌ها هم event (یک فاصله و بعد event) را اضافه کند و در جدول ذخیره نماید در غیر این صورت ارور مربوطه را برگرداند. امضای تابع به شکل زیر است:

func updateAppointment(db *gorm.DB, calendarName string, startTime time.Time, endTime time.Time, keyword string) error {
    // TODO: Implement
}
Go

اسکوپ UserWithRangeAppointment🔗

این اسکوپ باید تمام کاربرانی که زمان شروع ملاقات آن‌ها بین startDate و endDate است، موضوع ملاقات آن‌ها برابر subject است و اسم تقویم آن‌ها برابر calendarTableName را برگرداند. امضای اسکوپ به شکل زیر است:

func UserWithRangeAppointment(startDate, endDate time.Time, subject string, calendarTableName string) func(db *gorm.DB) *gorm.DB {
    // TODO: Implement
}
Go

نکات🔗

  • دقت کنید که قسمت‌های مختلف سوال با هم مرتبط هستند و در صورت صحیح نبودن جداول و روابط بین آن‌ها ممکن است تست‌های بعدی هم پاس نشوند!
  • شما در این سوال باید از GORM و امکانات آن استفاده کنید.
  • فایل db.go جهت اتصال GORM با دیتابیس است که در آن از دیزان‌پترن singleton استفاده شده و شما نیازی به تغییر آن ندارید و صرفا ثابت‌های آن را جهت اجرا در محیط لوکال خود می‌توانید تغییر دهید. فرض کنید سمت سرور نیز این فایل به درستی و مطابق دیتابیس سرور وجود دارد.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته شده، فایل main.go را که شامل تمام موارد گفته شده در بالا است آپلود کنید.

پیام رسان خسته



در این سوال میخوایم یک پیام رسان ساده با استفاده از گولنگ پیاده سازی کنیم. فقط این پیام رسان برای آن دسته از افرادی است که خیلی حوصله فضای مجازی را ندارند و دوست ندارند هربار که وارد برنامه میشوند با تعداد زیادی از پیام‌های جدید رو به رو شوند و به جایش دوست دارند هربار که خودشان خواستند سرور یکی از پیام‌های جدیدشان را (در صورت وجود) برایشان نمایش دهد.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به صورت زیر است:

.
├── go.mod
├── go.sum
├── message
│   └── message.go
├── server
│   └── server.go
├── test
│   └── sample_test.go
└── user
    └── user.go
Plain text

در هر کدام از فایل‌های go توابع و استراکت‌هایی وجود دارد که باید کاملشان کنید که در ادامه به آنها خواهیم پرداخت.

آن‌چه باید پیاده‌سازی کنید🔗

در این پروژه هر کاربر میتواند به کاربر یا گروهی دیگر پیام ارسال کند. فقط برای ارسال پیام از سمت سرور به گیرنده چندتا شرط وجود دارد.

  • اول اینکه هر کاربر فقط باید یک پیام دریافت نشده داشته باشد. یعنی اگر چند کاربر به یک کاربر پیام فرستاده باشند، تا وقتی کاربر گیرنده آن یک پیام را نخواند بقیه پیام‌ها به او ارسال نخواهند شد.
  • همچنین تا کاربر پیام را خواند نباید پیام بعدی برای او فرستاده شود. به این گونه که برای هر پیام یک زمانی در نظر گرفته میشود (مثلا n میلی‌ثانیه) و سرور هر n میلی‌ثانیه یک بار چک میکند که اگر گیرنده پیام خوانده نشده نداشته باشد، پیام جدید را به او ارسال میکند.
    • هر کاربر ممکن است subscribed باشد یا نباشد که با توجه به این ویژگی عدد n برای هر پیام از طرف او مشخص میشود. به این صورت که اگر subscribed باشد سرور هر 110 میلی ثانیه یک بار سعی میکند پیام او را به گیرنده بفرستد و اگر subscribed نباشد هر 290 میلی ثانیه یک بار. دقت کنید که این تایمر از زمان فرستادن هر پیام شروع میشود (یعنی هر پیام تایمر جدا دارد). همچنین هر بار 1 میلی ثانیه از مدت زمان تایمر کم میشود. یعنی اگر پیامی اولین بار پس از 110 میلی ثانیه سعی شود فرستاده شود و ناموفق بوده باشد (گیرنده پیام خوانده نشده از قبل داشته)، دفعه بعد پس از 109 میلی ثانیه دوباره سرور تلاش میکند ارسالش کند.

فایل message.go🔗

در این فایل استراکت حاوی اطلاعات پیام‌ها وجود دارد که نباید آن‌ را تغییر بدهید.

extensionFromNamemessage.go
type Message struct {
    FromID                   int
    ToID                     int
    Text                     string
    MsInQueueBeforeBeingSent time.Duration
}
Go

فیلد اول نشان دهنده آیدی کاربر فرستنده است. فیلد دوم آیدی کاربر گیرنده. فیلد سوم متن پیام است. فیلد چهارم نشان دهنده زمانی است که طول کشیده است تا پیام به سمت گیرنده ارسال شود (به میلی ثانیه). درباره فیلد آخر دقت کنید که هربار سرور سعی میکند پیام را به گیرنده ارسال کند باید آپدیت شود (یعنی با زمان سیستم نباید کار کنید).

فایل server.go🔗

در این فایل استراکت‌ زیر وجود دارد که باید خودتان کامل کنید.

extensionFromNameserver.go
type Server struct {

}
Go

همچنین تابع زیر را داریم که لیست کاربر و گروه‌ها را میگیرد و سرور را برمیگرداند.

extensionFromNameserver.go
func SetupServer(users []*user.User, groups []*user.Group) *Server {

}
Go

در ادامه تابع زیر را داریم که باید لیستی از پیام‌های موجود در صف سرور در آن لحظه را برگرداند. خروجی این تابع، ترتیب‌دار است. به این ترتیب پیامی که اول ارسال شده است باید اول لیست باشد.

extensionFromNameserver.go
func (s *Server) GetQueuedMessages() []message.Message {

}
Go

توجه: برای سرور امکان add و remove گروه‌ها و کاربر‌ها را نداریم. به این معنی که همان اطلاعاتی که موقع setup داشتیم، تا آخر برنامه معتبر است.

فایل user.go🔗

این فایل شامل دو دسته از استراکت‌ها و توابع است که باید آنها را پیاده سازی کنید. قسمت اول مربوط به کاربرها است.

extensionFromNameuser.go
type User struct {

}

func NewUser(id int, sub bool) *User {

}

func (u *User) SendMessage(receiver int, text string) error {

}

func (u *User) ReceiveMessage() message.Message {

}
Go

تابع NewUser دو مقدار به عنوان ورودی میگیرد. اولی آیدی کاربر است. دومی یک بولین است که نشان میدهد این کاربر جز کاربرهای subscribed است یا خیر. در آخر یوزر ساخته شده را برمیگرداند.

تابع SendMessage نیز دو مقدار به عنوان ورودی میگیرد. اولی آیدی کاربر گیرنده و دومی متن پیام است. همچنین اگر آیدی گیرنده در سرور وجود نداشت، این تابع باید یک ارور برگرداند (متن ارور مهم نیست).

تابع ReceiveMessage: این تابع در صورت وجود پیام جدید برای کاربر، آن را برمیگرداند. در غیر این صورت یک استراکت خالی برمی‌گرداند

قسمت دوم مربوط به گروه‌ها است (که کاربرها میتوانند عضو آن‌ها شوند)

extensionFromNameuser.go
type Group struct {

}

func NewGroup(id int, users []*User) *Group {

}

func (g *Group) AddUser(u *User) string {

}

func (g *Group) RemoveUser(u *User) string {

}
Go

تابع NewGroup: آیدی گروه و لیست کاربرهای موجود در این گروه را میگیرد. تضمین میشود آیدی هیچ گروهی با هیچ کاربری یکسان نیست.

تابع AddUser: یک کاربر را میگیرد و به گروه اضافه میکند. اگر کاربر در گروه نبود و با موفقیت به گروه اضافه شد رشته بالا و اگر از قبل عضو گروه بود رشته پایین را برمیگرداند.

User <ID> added to group <ID>
User <ID> already in group <ID>
Plain text

تابع RemoveUser: این تابع کاربر را از گروه حذف میکند. اگر کاربر در گروه بود و حذف شد پیام بالا و اگر اصلا عضو گروه نبود پیام پایین را برمیگرداند.

User <ID> removed from group <ID>
User <ID> isn't in group <ID>
Plain text

درباره فرستادن پیام به گروه‌ها:

  • اگر کاربر آیدی یک گروه را به عنوان گیرنده انتخاب کرده باشد، پیام باید به اعضای همه گروه فرستاده شود.
  • اگر کاربر فرستنده عضو گروه بود، پیام نباید به خودش فرستاده شود.
  • اعضای گروه هنگام ارسال پیام مهم هستند. یعنی اگر پیامی ارسال شد و سپس عضوی حذف شد پیام باید به او ارسال شود و اگر پیامی فرستاده شد و سپس کاربری به گروه اضافه شد آن پیام نباید برایش ارسال شود.
  • هر پیام که به گروه فرستاده میشود فقط یک بار به صف پیام‌ها اضافه میشود، نه به تعداد کاربرها (یعنی فقط همان پیامی که ToID آن برابر آیدی گروه است) و هروقت به همه اعضای گروه ارسال شد از صف حذف میشود. همچنین هربار که این پیام به یکی از اعضای گروه ارسال میشود، فیلد MsInQueueBeforeBeingSent این پیام در صف آپدیت میشود. به این معنی که اگر پیام برای بعضی از اعضا در 110 میلی ثانیه ارسال شد، این فیلد 110 میشود و... بدیهی است اگر پیام در یک زمان به همه اعضا ارسال شود از queue حذف میشود و دیگر به این پیام دسترسی نخواهیم داشت و نیازی به آپدیت این فیلد نیست.

تضمین‌ها🔗

  • کاربری وسط برنامه از سرور حذف یا به سرور اضافه نمیشود.
  • آیدی کاربرها و گروه‌ها هم پوشانی ندارد.
  • توابع پیاده سازی شده روی کاربرها و گروه‌ها فقط روی کاربرها و گروه‌های موجود در سرور صدا زده میشود. فقط در هنگام SendMessage لازم است چک کنید آیدی گیرنده در سرور وجود دارد یا خیر.

آن‌چه باید آپلود کنید🔗

یک فایل زیپ آپلود کنید که وقتی آن را باز می‌کنیم دایرکتوری‌های user که شامل user.go است و server که شامل server.go است را میبینیم.