Kibrit çöpü oyunu programı

Bir masa üzerine 1.000.000 (bir milyon) kibrit çöpü bırakılmıştır. İki kişi bu kibrit çöpleri ile aşağıdaki kurala göre oyun oynuyorlar. Sırası gelen oyuncu bir asal sayı p ve negatif olmayan bir n sayısı seçip masa üzerinden p^n tane kibrit çöpünü alabiliyor. (örneğin 8, 1, 25, 5 ,49, 125 vs) En son kibrit çöpünü alan oyuncu oyunu kazanıyor?

Bu oyuna uygun bir program yazmak istiyorum. Taktiği ve algoritması nasıl olmalı üzerinde konuşalım.

asallar = [n for n in range(2,1000000)if all([n%i for i in range(2,n)])]
kalancop = 1000000

def kullanici_kontrol(p,n):
    if p in asallar and n>1:
        return True
    else:
        return False   

def sistem_sayi(copler):
    # kalan çöplerden küçük, kurala uygun bir sayı üretilecek
    pass

print("""Bir masa üzerine 1.000.000 (bir milyon) kibrit çöpü bırakılmıştır.
      İki kişi bu kibrit çöpleri ile aşağıdaki kurala göre oyun oynuyorlar.
      Sırası gelen oyuncu bir asal sayı p ve negatif olmayan bir n sayısı seçip masa üzerinden p^n tane kibrit çöpünü alabiliyor.
      (örneğin 8, 1, 25, 5 ,49, 125 vs)
      En son kibrit çöpünü alan oyuncu oyunu kazanıyor.""")

while kalancop>0:
    p,n = map(int,input("Bir asal ve bir üs giriniz aralarında boşlukla.").split())
    if kullanici_kontrol(p,n):
        pass
    else:
        print("Geçersiz, lütfen yeniden girin")
        continue

    kalancop -= p**n
    sistem_sayi(kalancop)

Şöyle bir giriş yaptım. Ama hatalı olduğum yerleri söylemenize veya ekstra fikirlerinize açığım.

Tahmin ettiğim gibi ilk satır programın çalışmasını epey yavaşlattı. Şununla değiştirmeye karar verdim ama diğer yandan da sistemin sayı üretebilmesi için elimde bir asal tablosu olması gerektiğine inancım devam ediyor.

kalancop = 1000000

def asal(n):
  for i in range(2,int(n**0.5)+1):
    if (n%i) == 0:
      return False
  return True

def kullanici_kontrol(p,n):
    if asal(p) and n>=0 and p**n<kalancop:
        return True
    else:
        return False

   

def sistem_sayi(copler):
    # kalan çöplerden küçük, kurala uygun bir sayı üretilecek
    pass

print("""Bir masa üzerine 1.000.000 (bir milyon) kibrit çöpü bırakılmıştır.
      İki kişi bu kibrit çöpleri ile aşağıdaki kurala göre oyun oynuyorlar.
      Sırası gelen oyuncu bir asal sayı p ve negatif olmayan bir n sayısı seçip masa üzerinden p^n tane kibrit çöpünü alabiliyor.
      (örneğin 8, 1, 25, 5 ,49, 125 vs)
      En son kibrit çöpünü alan oyuncu oyunu kazanıyor.""")

while kalancop>0:
    p,n = map(int,input("Bir asal ve bir üs giriniz aralarında boşlukla.").split())
    if kullanici_kontrol(p,n):
        pass
    else:
        print("Geçersiz, lütfen yeniden girin")
        continue

    kalancop -= p**n
    sistem_sayi(kalancop)

Edit:
if asal(p) and n>=0 and p**n<kalancop şu satır düzelttim. örnek değerler içinde 1 olduğuna göre 0 da girilebiliyor, ayrıca girdiği sayıların kurala uyması kalan çöpten fazla olamaz.

Bir milyon kibrit çöpü var. Oyun sırası ilk bende. Asal sayım 2, çarpan sayım 500.000, bir milyon çöpü aldım oyun bitti.

Yani, p=2, n =500.000

Taktik basit, ilk oynama sırasını almaya bakın.

Kuralı açık anlatamadım hata bende.

Aldığın kibrit=p**n

Olmalı

Yok siz doğru anlatmışsınız. Ben bu işlemle çok ilerleyemeyiz onu anlatmak istedim. 1 Milyon’un asal çarpanları gibi bir durum oyun çok ilerlemez gibi geldi. Kısa sürer gibi geldi.

Sanki koşulları daha da zorlamak yada asal sayıyı yada n sayısını rasgele vermek yada bir aralık içinde tutmak gerekir gibi onu ifade etmeye çalıştım.

Yani asal çarpan kalmazsa kimin kaybedeceği gibi bir durum gerekiyor sanki.

EDIT:

Örneklemek gerekirse;

p:2 (Asal) n19 seçersem. Kafadan 2^19=524288 çöp alırım, sonraki hamlelerin anlamı kalmaz yarıdan fazlasını almış ve oyunu kazanmış olurum.

Amaç kalan kibritlerin asal üssü olmaması. Ama bi noktada mecbur kalınacak. Rakip de hepsini toplayacak.

Evet;

Bu burada durumu ifade etmişsiniz.

Her halükarda tek çöp bırakmak gerekecek. ki asal sayı kalmasın.

Ama o ana kadar ilk başlayan alabildiği bütün en yüksek asal çarpanlarla bunu sağlayabilir gibi duruyor.

Elimizd 2 gibi bir asal sayı olduğu sürece 2 çöp yada 1 çöp kalana kadar ilk başlayan avantajlı görünüyor.

Aynı asal sayıyı kullanmamak, bir şeyleri rastgele seçmek gibi bir kaç kısıt daha gerekebilir.

Aksi halde başarılı tek bir çözümü olan soru oluşur ve bu durumda, oyun monotonlaşır gibi duruyor.

Oyunun bir noktasında kalan çöp sayısı,125, 1048, 19683, 117649 vs olduğunda sırası gelen kazanabiliyor örneğin. Sadece 1,2 çöp kalmasa da olur. Kalan çöplerin bir asalın üssü olup olmadığına bakmalı program aynı zamanda.

Bunda mutabıkız, kodlama kısmı en kolay kısmı. Bu bir oyun olmaktan çok sabit çözümlü yada sabit kurallı bir çözüme yakın gibi duruyor. Yani bir tercih taktiği ile hep aynı kişi kazanabilir.

Oyun gibi bakmayın bunu kim nasıl kazanır bir çözüm üretin demek gibi bir durum var ortada. Anlatmak istediğim bu.

Koda tabi ki farklı şekilde asal çarpanlar ile asal sayılarla bir şeyler yapılır. Ama sürekli bir avantaj durumu var entropi eşit dağılmış gibi durmuyor. 3 Taş oynayıp ilk başlayıp hatalı oynamadığınız sürece ya kazanırsınız yada berabere kalır gibi bir durum oluyor sanki.

Bunu ifade etmeye çalışıyorum. Alınacak çöp sayısı mevcut çöplerin yarısını geçemez, aynı asal sayı iki kere kullanılamaz gibi biraz kısıtlama olması gerekir gibi düşünüyorum.

Taktiği üzerine konuşuyorum. Yoksa söylediğiniz şey kodlanabilir basit bir durum.

Asal bir sayı seç üssü için bir sayı seç, sonra çıkan sayı yerdeki çöpten azsa çıkar gibi bir durum kodlaması kolay.

Ama asal ve bir sayı ile sonlu bir çözüm kümesi gibi duruyor.

Bütün varyasyonları bile hesaplamak mümkün mü acaba diye düşünmeye başladım.

Hatta zaten asal sayı hesaplamaya da gerek var mı?

Sadece bir asal sayı tablosu tutup, bu tablodan mı verilen asal sayı diye kolayca da kontrol edilebilir mi her seferinde asal sayı hesaplamak da gerekir mi?

Gibi farklı kodlama fikirlerimde var. Sadece soru sanki çözümü sınırlı ve tekrarlanan hale gelmeye müsait gibi onu ifade ediyorum.

1 Beğeni

Anladım evet. Mutlaka bir triği vardır. Sayı o yüzden bir milyon verilmiştir. Kolay hesaplanamasın diye. En azından insan insana oynarken.

Şu oyuna benziyor teorik olarak. 1 ile 11 arasında bir sayı söyleyip 100 çöpten alıyoruz. Son çöpü alan kazanıyor. Burada 12 nin katlarını yakalayan kazanıyordu yanlış hatırlamıyorsam.

Ama en azından ilk başta şunları kontrol etmesi biraz daha adaletli olur. Kalan kibrip çöpü bir asalın üssüyse alsın hepsini değilse rastgele bir asal ve üs belirlesin, ama kendisi aldıktan sonra yine kalan çöpler yine bir asalın üssüyse başka iki sayı belirlesin.

1 Beğeni

Tam da bunu söylemek istedim. Yoksa ilk haliyle sorunuzun kodu.

Bir asal bir doğal sayı al, bu sayıları manipüle et (çarp, topla, böl yada üssünü al) Sonra mevcut tan eksilt.

Bu da gayet basit bir kod olur. Oyun kuralı karmaşlıklaştırarak kazanma şansı nasıl adil hale getirileiblir matematiksel analizi bu kısımda olmalı diye düşündüm.

İfade etmek istediğim bu idi. Ha sizin verdiğiniz kurala göre en anlamlı hamleler ne olurd o da analiz edilebilir ve zaten analiz ediliyorsa oyun çözümlenebilir oluyor gibi tabi dikkatle hesaplayıp bakmak da gerekebilir. Belki dengeyi bozan marjinal bir durum vardır.

Neden bunu düşündüm alakası gelebilir ama,

The Simplest Math Problem No One Can Solve - Collatz Conjecture - YouTube

Bir yerden sonra hesaplamalar hep aynı noktaya dönüyor,

4,2,1 döngüsüne.

İzlemek isteyebilirsiniz, matematik sevdalısı görüyorum sizi.

1 Beğeni

Collatz serisine bayılırım tabiki. Hala ispatlanamamış serilerden birisidir.

Programın son hali şu, rastgele oynuyor tabi kaybediyor genelde. :slight_smile: Asal tablosu da çok dar.

import random

kalancop = 1000000

def asal(n):
  for i in range(2,int(n**0.5)+1):
    if (n%i) == 0:
      return False
  return True

def kullanici_kontrol(p,n):
    if asal(p) and n>=0 and p**n<=kalancop:
        return True
    else:
        return False
    
def sistem_sayi(copler):
    # kalan çöplerden küçük, kurala uygun bir sayı üretilecek
    asaltablosu = [2,3,5,7,11,13,17]
    rast_asal = random.choice(asaltablosu)
    rast_us = random.randint(0,10)
    while rast_asal**rast_us>kalancop:
        rast_asal = random.choice(asaltablosu)
        rast_us = random.randint(1,10)
    return rast_asal,rast_us

print("""Bir masa üzerine 1.000.000 (bir milyon) kibrit çöpü bırakılmıştır. 
      İki kişi bu kibrit çöpleri ile aşağıdaki kurala göre oyun oynuyorlar. 
      Sırası gelen oyuncu bir asal sayı p ve negatif olmayan bir n sayısı seçip masa üzerinden p^n tane kibrit çöpünü alabiliyor. 
      (örneğin 8, 1, 25, 5 ,49, 125 vs) 
      En son kibrit çöpünü alan oyuncu oyunu kazanıyor.""")

while kalancop>0:
    p,n = map(int,input("Bir asal ve bir üs giriniz aralarında boşlukla.").split())
    if kullanici_kontrol(p,n):
        kalancop -= p**n
        print(f"Girdiğiniz sayılar {p} ^ {n} ve kalan çöp sayısı {kalancop}")
    else:
        print("Geçersiz, lütfen yeniden girin")
        continue
    
    sp,sn= sistem_sayi(kalancop)
    kalancop -= sp**sn
    print(f"Söylediğim sayılar {sp} ^ {sn} ve kalan çöp sayısı {kalancop}")

sistemsayi(copler) , copleri parametre aldım bunun sebebi kullanmaktı işte sonraki adımlar için hesaplama vs için. Yapacağım o kısmı daha.

1 Beğeni

Mesela 999983 sayısı asal, ama benim tablomda yok. Kalap çöp sayısı 999983 olsaydı, oyunu 999983 1 diyerek kazanmış olacaktı program.

1 milyona kadar asalları bir listede tutmak yerine, kalan sayının bir asalın üssü olup olmadığına kısa yoldan bakmanın bir algoritması var mı?

Edit: Sorarken farkettim, asal çarpanlarına ayırmak işe yarabilir kalan kibrit sayısını. Onu da yaptım asal çarpan sayısı 1 ise bir asalın üssü olduğu anlamına geliyor.

import random

kalancop = 1000000

def asal(n):
  for i in range(2,int(n**0.5)+1):
    if (n%i) == 0:
      return False
  return True

def asal_carpan_sayisi(sayi):
   carpanlar = []
   bolen = 2
   while sayi>1:
      if sayi%bolen == 0:
        carpanlar.append(bolen)
        sayi = sayi / bolen
        bolen -= 1
      bolen +=1
   return bolen,len(set(carpanlar))
      

def kullanici_kontrol(p,n):
    if asal(p) and n>=0 and p**n<=kalancop:
        return True
    else:
        return False
    
def sistem_sayi(copler):
    # kalan çöplerden küçük, kurala uygun bir sayı üretilecek
    sayi,carpanlar = asal_carpan_sayisi(copler)
    if carpanlar==1:
       pass # burada sayi değişkenini uygun üsle geri gönderme planım var.
    
    asaltablosu = [2,3,5,7,11,13,17]
    rast_asal = random.choice(asaltablosu)
    rast_us = random.randint(0,10)
    while rast_asal**rast_us>kalancop:
        rast_asal = random.choice(asaltablosu)
        rast_us = random.randint(1,10)
    return rast_asal,rast_us

print("""Bir masa üzerine 1.000.000 (bir milyon) kibrit çöpü bırakılmıştır. 
      İki kişi bu kibrit çöpleri ile aşağıdaki kurala göre oyun oynuyorlar. 
      Sırası gelen oyuncu bir asal sayı p ve negatif olmayan bir n sayısı seçip masa üzerinden p^n tane kibrit çöpünü alabiliyor. 
      (örneğin 8, 1, 25, 5 ,49, 125 vs) 
      En son kibrit çöpünü alan oyuncu oyunu kazanıyor.""")

while kalancop>0:
    p,n = map(int,input("Bir asal ve bir üs giriniz aralarında boşlukla.").split())
    if kullanici_kontrol(p,n):
        kalancop -= p**n
        print(f"Girdiğiniz sayılar {p} ^ {n} ve kalan çöp sayısı {kalancop}")
    else:
        print("Geçersiz, lütfen yeniden girin")
        continue
    
    sp,sn= sistem_sayi(kalancop)
    kalancop -= sp**sn
    print(f"Söylediğim sayılar {sp} ^ {sn} ve kalan çöp sayısı {kalancop}")

Bu da bizi şu açmaza götürür, herkes belirli bir sınıra kadar en küçük asal üssü bir sayı söyleme çabasına götürür. Taki o sınırdaki asal sayıyı söyleyip son kalanı alana kadar.

Bu da ikinci bir kural ihtiyacının altını çizer asal üzeri bir doğal sayı.

üzeri 1 yada üzeri 0 hileli durumlar. En az karesi olduğunda bu kadar büyük bir asal tablosu oluşmayacaktır. Karesi 1 milyon u geçmeyen asal sayı max asal sayıdır. Bu da bizi 1000x1000 gibi bir durumda zaten binden küçük ilk asal sayıya götürür. Yani üsü sıfır ve üssü bir hariç tutarsanız. Max asal sayınız binden küçük bir tablo olacaktır.

Kural gereği üzeri 0 veya 1 denebiliyor. Hariç tutamıyorum bu yüzden.

Bu kural olduğu sürece oyun rahatlıkla asalların savaşı olarak daha da analiz edilebilir hale gelir ki bu şartlarda 2^0 ve 2^1 ile 2 ve bir eksilterek birinci kurala kadar rakibi eksiltmeye zorlar max sınıra gelince de alırsınız.

Sanki bunu analiz etmek daha kolay.

EDIT bu durumda oyun 0-17 aralığına sıkışır. Karşılıklı 1 ve 2 eksilterek 17 ye ulaşıldığında yada geçildiğinde diğeri 999983 dediğinde oyun biter.

Bir şey eksik rastgelelik oluşmuyor sanki. Satrançtaki gibi her hamlenin farklı kısıtı olmalı ki varyasyon çeşitlensin.

Neden ki? Atıyorum oyunu 5^3 ile açtım, o sayıyı geçtiğimiz için sorun teşkil etmiyor.

Bu sefer bir milyon-5^3 üzerinde yine en büyük asal arasına sıkıştırmaya çalışırsınız.

Sonra bir asala denk gelmeden 2^0 , 2^1 döngüsüne tekrar döneriz.

EDIT:

Aklıma bir fikir getirdiniz.

Şimdi şöyle bir ile 1 milyon arasındaki asal sayıları bir alayım. Bir metin dosyası yada tabloya yerleştireyim.

Sonra ihtmaller üzerine bir karar robotu yada çözümleme önerisi çıkarabilen bir kod yazabilir miyiz bakalım. Zaten her asal üzeri sayı kullanılduğında benim başlangıç tablomdaki max asal sayı miktarı azalacaktır.

Sonra masada kalan sayı ile vereceğim max sayı arasındaki fark asal değilse oyna değilse bir küçük asala geç gibi bir yaklaşım üzerinde bir çözümleme çıkarabilir mi görmeye çalışayım.

Asalları saydırayım bakalım kaç asal alternatifimiz var görelim. Her hamlede zaten azalacak buradan bir yere varıyor muyum bakar bir kod oluşursa paylaşırım.

EDIT 2:

Kendime ve meraklılarına not. Tek tek tüm asalları 1 milyona kadar hesaplatmayı bekleyecek kadar delirmedim.

The first fifty million primes (t5k.org)

Burada belirli aralıklarda asal sayıların dosyaları var. Başlangıç noktası olarak olarak bu tür bir dosya kullanacağım.

İlk dosya: 2 15,485,863 aralığında bana 1 Milyona kadar ki kısmı lazım. Kırptım.

Yaklaşık olarak saydığımda: Kolonlar 8 erli ve ve son satırdan bir önceki satır numarası, 9812 ve en son satırda 2 sayı daha var. 78.498 sayılık bir metin dosyam var.

Artık bir sayı asal mı diye bakmak için bu dosyada aralık yarılama metodu ile basit bir arama yaparak tabloda yoksa hızlı bir şekilde asal olup olmadığını bulabileceğim. Tabi belki yanılıyoruz asal mı diye kontrol etmek daha kısa sürebilir. Bir zaman tutar ikisini de karşılaştırabiliriz.

Tabi tablo 8 sütun bunu bir algoritma ile her sayıyı bir satıra mı indirsem satırda 8 grup içinde mi arasam henüz karar veremedim. Ama buradan bir asal look up table çıkar gibi geliyor bana.

1 Beğeni

Dosya fikri güzel, üslü katlarını da yazdırırsak kolaylaşır gibi.