пятница, 31 октября 2025 г.

 Программирование игры в уголки.




Это старая бородатая задача, которую можно решить перебором на глубину 3..5 для одной стороны, т.е. ходы противника не учитываются. Но если отойти от примитивизма, до задача становится интересной, потому как одна сторона должна играть в полную силу а ходы другой учитываться, но не во всех случаях.

Правила игры.




Шашки ставятся в углы, и целью является занять угол противника. Шашки могут ходить на одно поля или прыгать через шашки, свои и противника.

Оценка позиции.

Оценкой может служить просто индекс 0..63 массива для верхних (черных) и инвертированный индекс 63-n для белых. Целевые поля получают премию, свои поля - небольшой штраф, чтобы программа их быстрее оставляла. Центр может получить небольшую премию.
Перед каждым ходом можно инициализировать генератор случайных чисел системным временем и давать для каждой клетки небольшой случайный бонус.

 


///// игра  уголки - основные структры данных и алгоритмы///
#define WHITE 0
#define BLACK 1
#define PAWN 1
#define NOPIECE 0

int pos[64];//фигуры
int color[64];//их цвет
int score[64]={
 0, 0, 0, 4, 5, 6, 7, 8, 
 1, 1, 1,12,13,14,15,16, 
 2, 2, 2,20,21,22,23,24, 
25,26,27,30,31,30,31,32,  
33,34,35,38,39,38,39,40, 
41,42,43,44,45,62,63,64, 
49,50,51,52,53,65,66,67,  
57,58,59,60,61,68,69,70};

int random_score[64];

void InitScore(){
  srand(time(0));
  for(int sq=0;sq<64;sq++)
   random_score[j] = random()%2;
}

int Evaluate(int color, //относительно кого оценка
             int player_color,//цвет игрока
             int ply //глубина поиска
             ){

  int s[2]={0,0}; 

  for(int sq=0;sq<64;sq++)
   if(pos[sq] != NOPIECE)
   {
     if(color[sq]==BLACK)
       s[BLACK] += score[sq] + random_score[sq];
     else  
       s[WHITE] += score[63-sq] + random_score[63-sq];
   }

  if(s[color]>60*9) return INFINITY-ply;//конец игре
  if(c[color^1]>60*9) return -INFINITY+ply;


  //одна из сторон получает большую оценку, чтобы
  //не 'зажимались' фигуры
  
  if(color==player_color)
    return s[color]/3 - s[color^1];
  else
    return s[color] - s[color^1]/3;

 

Поиск.

Здесь я применил модифицированный вариант - первый лучший. Надо учесть что функция поиска принимает аргумент - лучший ход и начинает поиск с него и возвращает тоже лучший ход и его оценку. Без этого алгоритм первый - лучший теряет смысл, так как первый ход должен быть осмысленным.


int search(int alpha, int beta, int depth, int color, Move* best){
 Move m=0; 
 if(depth<=0)return Evaluate(color);
 GenerateMoves();
 PickBestMove(*best);
 int cnt_moves=0;
 for(mv in moves){
   cnt_moves++;
   MakeMove(mv);
   if(depth<=2) 

     tmp=-search(-beta,-alpha,depth-1,color^1,&m);
   else{
     for(int d=max(2,(int)sqrt(depth)); d <= depth-1; d++)
     {
       tmp=-search(-beta,-alpha,d,color^1,&m);
       if(tmp<=alpha && cnt_moves>1)break;
     }
   
   }
   
   UnMakeMove(mv);
   if(tmp>alpha) { alpha=tmp; *best=mv;}
   if(alpha>=beta) break;
 }
 return alpha;
}
/////

Заключение.

Этот алгоритм будет работать даже для несложных шахмат, только шахи в оконечном поиске надо расширять (глубину их поиска).
В зключении прилагаю исходники программы, написанно еще на turbo pascal 7.1. Можно запускать под dosemu.
https://disk.yandex.ru/d/QNDvuEpVQVNybg




      



четверг, 23 октября 2025 г.

Программирование игры в русские шашки.



  Статья pdf

В добавление к статье. В шашках как и в шахматах может проявляться эффект горизонта. Совершенно немыслимо написать шахматы не просматривая глубже шахи. Иначе программа может ломаться и сливать игру. В шашках тоже самое. Взятия являются вынужденными ходами и их можно просматривать несколько глубже примерно так. 

      int M = mtl[WHITE] - mtl[BLACK];

       if(side==WHITE){

           if(M >= white_mtl_search_path[ply-5])

             extend++;

       }else{

           if(M <= white_mtl_search_path[ply-5])

             extend++;

       }

Программа будет глубже смотреть строки с разменами и ввигрышами материала. 

Успехов. 

суббота, 11 октября 2025 г.

Логическая задача

 Пример 1.15. Пытаясь вспомнить победителей прошлогоднего турнира, пять бывших зрителей турнира заявили: 1) Антон был вторым, а Борис – пятым. 2) Виктор был вторым, а Денис – третьим. 3) Григорий был первым, а Борис - третьим 4) Антон был третьим, а Евгений – шестым. 5) Виктор был третьим, а Евгений – четвертым. Впоследствии выяснилось, что каждый зритель ошибся в одном из двух своих высказываний. Каково было истинное распределение мест в турнире? Решение. Обозначим высказывания зрителей символом Ху , где Х – первая буква имени участника турнира, а у – номер места, которое он занял в турнире. Так как в паре высказываний каждого зрителя одно истинно, а второе ложно, то будут истинными дизъюнкции этих высказываний А ∨ БK ≡ 1; В ∨ Д ≡ 1; Г ∨ Б ≡ 1; А ∨ ЕO ≡ 1; В ∨ Е ≡ 1. Но тогда истинной будет формула 8 ≡ (- ∨ БK) ∧ (В ∨ Д ) ∧ (Г ∨ Б) ∧ (А ∨ ЕO) ∧ (В ∨ Е) ≡ 1. Путем равносильных преобразований легко показать, что 8 ≡ А ∧ БK ∧ В ∧ Г ∧ Е ≡ 1. Откуда получаем А ≡ 1, БK ≡ 1, В ≡ 1, Г ≡ 1; Е ≡ 1, что и дает ответ задачи

Игра в дурня на python. Deep seek

 Отличная идея! Минимакс с альфа-бета отсечением значительно улучшит силу игры компьютера. Вот улучшенная версия с минимаксом:


```python

import random

from typing import List, Tuple, Optional, Dict

from collections import defaultdict

import math


class Card:

    def __init__(self, rank: str, suit: str):

        self.rank = rank

        self.suit = suit

        self.value = self._get_value()

    

    def _get_value(self) -> int:

        values = {'6': 6, '7': 7, '8': 8, '9': 9, '10': 10, 

                 'J': 11, 'Q': 12, 'K': 13, 'A': 14}

        return values[self.rank]

    

    def __str__(self):

        return f"{self.rank}{self.suit}"

    

    def __repr__(self):

        return str(self)

    

    def __eq__(self, other):

        return self.rank == other.rank and self.suit == other.suit

    

    def __hash__(self):

        return hash((self.rank, self.suit))


class Deck:

    def __init__(self):

        ranks = ['6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']

        suits = ['♥', '♦', '♣', '♠']

        self.cards = [Card(rank, suit) for suit in suits for rank in ranks]

        random.shuffle(self.cards)

    

    def deal(self, num_cards: int) -> List[Card]:

        return [self.cards.pop() for _ in range(num_cards)]


class GameState:

    """Класс для представления состояния игры"""

    def __init__(self, players_hands: List[List[Card]], table: List[Card], 

                 trump: str, attacker: int, deck_size: int):

        self.players_hands = [hand.copy() for hand in players_hands]

        self.table = table.copy()

        self.trump = trump

        self.attacker = attacker

        self.defender = 1 - attacker

        self.deck_size = deck_size

    

    def copy(self):

        return GameState(self.players_hands, self.table, self.trump, 

                        self.attacker, self.deck_size)


class FoolGameWithMinimax:

    def __init__(self):

        self.deck = Deck()

        self.players = [[], []]  # [0] - компьютер, [1] - игрок

        self.trump = self.deck.cards[-1].suit

        self.table = []

        self.attacker = 1  # кто ходит первым

        self.defender = 0  # кто отбивается

        self.max_depth = 3  # глубина поиска минимакса

        

    def deal_initial_cards(self):

        for i in range(2):

            self.players[i] = self.deck.deal(6)

            self.sort_hand(self.players[i])

    

    def sort_hand(self, hand: List[Card]):

        hand.sort(key=lambda card: (card.suit != self.trump, card.value))

    

    def get_game_state(self) -> GameState:

        return GameState(self.players, self.table, self.trump, 

                        self.attacker, len(self.deck.cards))

    

    def evaluate_state(self, state: GameState, player: int) -> float:

        """Оценка состояния игры для указанного игрока"""

        if not state.players_hands[player]:

            return 1000  # выигрыш

        

        if not state.players_hands[1 - player]:

            return -1000  # проигрыш

        

        score = 0

        

        # Оценка по картам в руке

        for i, hand in enumerate(state.players_hands):

            player_factor = 1 if i == player else -1

            hand_value = sum(card.value for card in hand)

            

            # Бонус за козыри

            trump_count = sum(1 for card in hand if card.suit == state.trump)

            trump_bonus = trump_count * 5

            

            # Штраф за много высоких карт (их сложнее сбросить)

            high_cards_penalty = sum(max(0, card.value - 10) for card in hand) * 0.5

            

            score += player_factor * (hand_value + trump_bonus - high_cards_penalty)

        

        # Оценка стола

        if state.table:

            if state.attacker == player:

                # Мы атакуем - хорошо иметь много карт на столе

                score += len(state.table) * 2

            else:

                # Мы защищаемся - плохо иметь много карт на столе

                score -= len(state.table) * 3

        

        # Бонус за карты в колоде

        score += state.deck_size * 0.1 * (1 if player == state.attacker else -1)

        

        return score

    

    def minimax(self, state: GameState, depth: int, alpha: float, beta: float, 

                maximizing_player: bool) -> Tuple[float, Optional[Tuple[Card, Optional[Card]]]]:

        """Минимакс с альфа-бета отсечением"""

        if depth == 0 or self.is_terminal_state(state):

            return self.evaluate_state(state, 0), None  # 0 - компьютер

        

        current_player = state.attacker if not state.table else state.defender

        is_computer_turn = (current_player == 0)

        

        if (maximizing_player and is_computer_turn) or (not maximizing_player and not is_computer_turn):

            return self._max_value(state, depth, alpha, beta, maximizing_player)

        else:

            return self._min_value(state, depth, alpha, beta, maximizing_player)

    

    def _max_value(self, state: GameState, depth: int, alpha: float, beta: float, 

                  maximizing_player: bool) -> Tuple[float, Optional[Tuple]]:

        best_value = -math.inf

        best_move = None

        

        for move in self.get_possible_moves(state):

            new_state = self.apply_move(state, move)

            value, _ = self.minimax(new_state, depth - 1, alpha, beta, not maximizing_player)

            

            if value > best_value:

                best_value = value

                best_move = move

            

            alpha = max(alpha, best_value)

            if beta <= alpha:

                break

        

        return best_value, best_move

    

    def _min_value(self, state: GameState, depth: int, alpha: float, beta: float, 

                  maximizing_player: bool) -> Tuple[float, Optional[Tuple]]:

        best_value = math.inf

        best_move = None

        

        for move in self.get_possible_moves(state):

            new_state = self.apply_move(state, move)

            value, _ = self.minimax(new_state, depth - 1, alpha, beta, not maximizing_player)

            

            if value < best_value:

                best_value = value

                best_move = move

            

            beta = min(beta, best_value)

            if beta <= alpha:

                break

        

        return best_value, best_move

    

    def get_possible_moves(self, state: GameState) -> List[Tuple[Card, Optional[Card]]]:

        """Возвращает все возможные ходы для текущего состояния"""

        moves = []

        current_player = state.attacker if not state.table else state.defender

        

        if not state.table:  # Атака

            # Можно пойти любой картой

            for card in state.players_hands[current_player]:

                moves.append((card, None))

        else:  # Защита или дополнительная атака

            if current_player == state.defender:  # Защита

                attacking_card = state.table[-1]

                for card in state.players_hands[current_player]:

                    if self._can_beat(attacking_card, card, state.trump):

                        moves.append((card, None))

                

                # Также можно взять карты

                moves.append((None, None))

            else:  # Дополнительная атака

                table_ranks = {card.rank for card in state.table}

                for card in state.players_hands[current_player]:

                    if card.rank in table_ranks:

                        moves.append((card, None))

        

        return moves

    

    def apply_move(self, state: GameState, move: Tuple[Card, Optional[Card]]) -> GameState:

        """Применяет ход к состоянию и возвращает новое состояние"""

        new_state = state.copy()

        card, _ = move

        

        if card is None:  # Взять карты

            new_state.players_hands[new_state.defender].extend(new_state.table)

            new_state.table.clear()

            new_state.attacker, new_state.defender = new_state.defender, new_state.attacker

        else:  # Сходить картой

            # Убираем карту из руки игрока

            new_state.players_hands[new_state.attacker if not new_state.table else new_state.defender].remove(card)

            new_state.table.append(card)

            

            # Если защита успешна и карты закончились - очищаем стол

            if new_state.table and len(new_state.table) % 2 == 0:

                if not self.can_continue_attack(new_state):

                    new_state.table.clear()

                    new_state.attacker, new_state.defender = new_state.defender, new_state.attacker

        

        return new_state

    

    def can_continue_attack(self, state: GameState) -> bool:

        """Можно ли продолжать атаку"""

        if not state.table or len(state.players_hands[state.attacker]) == 0:

            return False

        

        table_ranks = {card.rank for card in state.table}

        return any(card.rank in table_ranks for card in state.players_hands[state.attacker])

    

    def is_terminal_state(self, state: GameState) -> bool:

        """Проверка конечного состояния"""

        return (not state.players_hands[0] or not state.players_hands[1] or 

                (not state.deck_size and not state.table and 

                 (not state.players_hands[state.attacker] or not state.players_hands[state.defender])))

    

    def _can_beat(self, attacking: Card, defending: Card, trump: str) -> bool:

        """Может ли защищающая карта побить атакующую"""

        if defending.suit == trump and attacking.suit != trump:

            return True

        elif defending.suit == attacking.suit and defending.value > attacking.value:

            return True

        return False

    

    def find_best_move_minimax(self) -> Tuple[Optional[Card], Optional[Card]]:

        """Находит лучший ход с помощью минимакса"""

        state = self.get_game_state()

        _, best_move = self.minimax(state, self.max_depth, -math.inf, math.inf, True)

        return best_move or (None, None)

    

    def make_computer_move(self):

        """Компьютер делает ход на основе минимакса"""

        print("Компьютер думает...")

        card_to_play, _ = self.find_best_move_minimax()

        

        if card_to_play:

            self.players[0].remove(card_to_play)

            self.table.append(card_to_play)

            print(f"Компьютер ходит: {card_to_play}")

            return True

        else:

            print("Компьютер берет карты")

            self.players[0].extend(self.table)

            self.table.clear()

            self.sort_hand(self.players[0])

            return False

    

    def make_player_move(self, card_index: int) -> bool:

        """Игрок делает ход"""

        if 0 <= card_index < len(self.players[1]):

            card = self.players[1][card_index]

            self.players[1].remove(card)

            self.table.append(card)

            print(f"Вы ходите: {card}")

            return True

        return False

    

    def player_defend(self, card_index: int) -> bool:

        """Игрок защищается"""

        if not self.table:

            return False

            

        attacking_card = self.table[-1]

        if 0 <= card_index < len(self.players[1]):

            defending_card = self.players[1][card_index]

            

            if self._can_beat(attacking_card, defending_card, self.trump):

                self.players[1].remove(defending_card)

                self.table.append(defending_card)

                print(f"Вы отбиваете: {defending_card}")

                return True

        

        print("Нельзя побить этой картой!")

        return False

    

    def replenish_hands(self):

        """Добираем карты из колоды"""

        for i in range(2):

            while len(self.players[i]) < 6 and self.deck.cards:

                new_card = self.deck.deal(1)[0]

                self.players[i].append(new_card)

            self.sort_hand(self.players[i])

    

    def display_game_state(self):

        """Показывает текущее состояние игры"""

        print(f"\n{'='*50}")

        print(f"Козырь: {self.trump}")

        print(f"Карт в колоде: {len(self.deck.cards)}")

        print(f"\nСтол: {self.table}")

        print(f"\nВаши карты:")

        for i, card in enumerate(self.players[1]):

            print(f"{i}: {card}")

        print(f"\nКарты компьютера: {len(self.players[0])} карт")


def main():

    game = FoolGameWithMinimax()

    game.deal_initial_cards()

    

    print("Добро пожаловать в игру 'Дурак' с минимакс-ИИ!")

    print("Компьютер использует алгоритм минимакс для поиска лучшего хода.")

    

    while True:

        game.display_game_state()

        

        if game.attacker == 1:  # ход игрока

            if not game.table:  # атака

                print("\nВаш ход (атака). Выберите карту:")

                try:

                    choice = int(input("Введите номер карты: "))

                    if game.make_player_move(choice):

                        game.attacker, game.defender = game.defender, game.attacker

                except (ValueError, IndexError):

                    print("Неверный ввод!")

                    continue

            else:  # дополнительная атака

                print("\nХотите подкинуть карту? (y/n/t - взять)")

                action = input().lower()

                if action == 'y':

                    print("Выберите карту для подкидывания:")

                    try:

                        choice = int(input("Введите номер карты: "))

                        if game.make_player_move(choice):

                            pass

                        else:

                            continue

                    except (ValueError, IndexError):

                        print("Неверный ввод!")

                        continue

                elif action == 't':

                    print("Вы берете карты")

                    game.players[1].extend(game.table)

                    game.table.clear()

                    game.sort_hand(game.players[1])

                    game.replenish_hands()

                    game.attacker = 0

                    game.defender = 1

                else:

                    # Завершаем раунд

                    game.table.clear()

                    game.replenish_hands()

                    game.attacker = 0

                    game.defender = 1

        

        else:  # ход компьютера

            if not game.table:  # атака

                print("\nХод компьютера (атака):")

                if game.make_computer_move():

                    game.attacker, game.defender = game.defender, game.attacker

                else:

                    game.table.clear()

                    game.replenish_hands()

            else:  # защита компьютера

                print("\nХод компьютера (защита):")

                if game.make_computer_move():

                    pass

                else:

                    print("Компьютер берет карты")

                    game.players[0].extend(game.table)

                    game.table.clear()

                    game.sort_hand(game.players[0])

                    game.replenish_hands()

                    game.attacker = 1

                    game.defender = 0

        

        # Проверка конца игры

        if not game.players[0] and not game.players[1]:

            print("Ничья!")

            break

        elif not game.players[0]:

            print("Компьютер проиграл! Вы выиграли!")

            break

        elif not game.players[1]:

            print("Вы проиграли! Компьютер выиграл!")

            break


if __name__ == "__main__":

    main()

```


**Основные улучшения с минимаксом:**


1. **Класс GameState** - изолированное состояние игры для поиска

2. **Минимакс с альфа-бета отсечением** - просчет ходов на 3 шага вперед

3. **Функция оценки** учитывает:

   - Количество и ценность карт

   - Козыри

   - Позицию на столе

   - Карты в колоде


4. **Генерация всех возможных ходов** для каждого состояния

5. **Применение ходов** к копиям состояния для симуляции


**Преимущества минимакса:**

- Компьютер думает на несколько ходов вперед

- Учитывает ответные ходы игрока

- Выбирает стратегически выгодные позиции

- Избегает ловушек и использует слабости игрока


**Настройки:**

- `max_depth = 3` - можно увеличить для более сильной игры (но дольше расчет)

- Функцию оценки можно дополнить более сложными эвристиками


Теперь компьютер играет на качественно новом уровне!

суббота, 4 октября 2025 г.

Игра в Уголки на Bisual Basic 6


 

 Игра в уголки играется шашками. Надо занять поле противника ходя на одну клетку или перепрыгивая через. Все просто. Перебор ведется на 3 полухода. Хд противника можно не учитывать. Вводится некоторая случайность в оценке каждой клетки - случайность в пределах разумного.

Скачать исходники и исполняемый файл 

Для запуска программы могут потребоваться DLL Visual Basic 6. Придется его установить. Он очень небольшой по объему. 

Пример кода главного модуля:

Attribute VB_Name = "frmMain"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
'Corners 2012.02.11
'Wrote Nifont I.,

Const black_pawn = 1
Const white_pawn = 2
Const empty_square = 0

Dim init_pos ' 0..63
Dim click_sq(0 To 1) As Integer

Dim pos(0 To 63) As Integer
Const MaxPly = 10
Dim tree(0 To 1000) As move_type
Dim tree_cnt(0 To MaxPly) As Integer
Dim ply As Integer

Dim exist(0 To 63) As Integer

Const Max_Pieces = 9

Dim black_score_tbl ' (0 to 63)
Dim black_score_tbl_init
Dim target_tbl '      (0 to 63)
Dim static_score(1 To 2) As Integer
Dim end_piece_cnt(1 To 2) As Integer
Dim start_piece_cnt(1 To 2) As Integer
Dim xside(1 To 2) As Integer
Const INF = 30000
Dim find_best(0 To MaxPly) As move_type


Sub mix(ByVal min As Integer, ByVal max As Integer)
 Dim a%, b%, j%
 Dim mv As move_type
 
 If min < max Then
  Randomize
  For j = 0 To 20
    a = Int((max - min) * Rnd + min)
    b = Int((max - min) * Rnd + min)
    mv = tree(a)
    tree(a) = tree(b)
    tree(b) = mv
  Next j
 End If
End Sub



Function search(ByVal d As Integer, _
                ByVal side As Integer _
                ) As Integer
  Dim j%, score%
 
  find_best(ply).a = 0
  find_best(ply).b = 0
 
  If is_end_game > 0 Then
     search = (INF - ply)
  ElseIf (d <= 0) Or (ply >= MaxPly - 1) Then
     search = evaluate(side)
  Else
     search = -INF
     generate (side)
     If ply = 0 Then mix 0, tree_cnt(1) - 1
     
     For j = tree_cnt(ply) To tree_cnt(ply + 1) - 1
            
       make_move tree(j).a, tree(j).b
       ply = ply + 1
            
       score = search(d - 1, side)
       ply = ply - 1
       un_make_move tree(j).a, tree(j).b
       If score > search Then
         search = score
         find_best(ply) = tree(j)
       End If
     Next j
  End If
End Function



Function search_best(ByVal side) As Boolean
  Dim score%, d%, j%
  
   d = 3
   If end_piece_cnt(black_pawn) >= Max_Pieces - 1 Then
     d = 4
   End If
   
   score = search(3, side)
   
  search_best = find_best(0).a <> find_best(0).b
End Function


Function is_end_game() As Integer
  If (end_piece_cnt(white_pawn) >= Max_Pieces) Then
     is_end_game = white_pawn
  ElseIf (end_piece_cnt(black_pawn) >= Max_Pieces) Then
     is_end_game = black_pawn
  Else
     is_end_game = 0
  End If
End Function

Function evaluate(ByVal side As Integer) As Integer
 Dim score%
 
 score = static_score(white_pawn) - _
         static_score(black_pawn)
         
 If frmMain.optMaster.Value = True Then
   If start_piece_cnt(white_pawn) = 0 Then
     score = score + 70
   End If
 
   If start_piece_cnt(black_pawn) = 0 Then
     score = score - 70
   End If
 End If
 
 
 If side = white_pawn Then
  evaluate = score
 Else
  evaluate = -score
 End If
End Function


Sub insert_piece(ByVal p As Integer, _
                 ByVal sq As Integer)
                 
                 

                 
  pos(sq) = p
  If p = black_pawn Then
    static_score(p) = _
      static_score(p) + black_score_tbl(sq) + _
      target_tbl(sq)
    
  Else 'white_pawn
    static_score(p) = _
      static_score(p) + black_score_tbl(63 - sq) + _
      target_tbl(63 - sq)
  End If
  
  If init_pos(sq) = xside(p) Then
    end_piece_cnt(p) = end_piece_cnt(p) + 1
  ElseIf init_pos(sq) = p Then
    start_piece_cnt(p) = start_piece_cnt(p) + 1
  End If
End Sub



Sub remove_piece(ByVal p As Integer, _
                 ByVal sq As Integer)
  pos(sq) = 0
  If p = black_pawn Then
    static_score(p) = _
      static_score(p) - black_score_tbl(sq) - _
      target_tbl(sq)

  Else 'white_pawn
    static_score(p) = _
      static_score(p) - black_score_tbl(63 - sq) - _
      target_tbl(63 - sq)
  End If
  
  If init_pos(sq) = xside(p) Then
    end_piece_cnt(p) = end_piece_cnt(p) - 1
  ElseIf init_pos(sq) = p Then
    start_piece_cnt(p) = start_piece_cnt(p) - 1
  End If
End Sub

                 
Sub make_move(a As Integer, b As Integer)
   Debug.Assert (pos(a) > 0) And (pos(b) = 0)
   
   p = pos(a)
   remove_piece p, a
   insert_piece p, b
End Sub

Sub un_make_move(a As Integer, b As Integer)

   Debug.Assert (pos(b) > 0) And (pos(a) = 0)
   
   p = pos(b)
   remove_piece p, b
   insert_piece p, a
End Sub



Sub link_move(a As Integer, b As Integer)

  Debug.Assert (pos(a) > 0) And (pos(b) = 0)

  With tree(tree_cnt(ply + 1))
    .a = a
    .b = b
  End With
  tree_cnt(ply + 1) = tree_cnt(ply + 1) + 1
End Sub

 

Sub jamp_piece(ByVal start_sq As Integer, _
               ByVal sq As Integer)
  
 If exist(sq) = 0 Then
  
  exist(sq) = 1
  If sq <> start_sq Then
    link_move start_sq, sq
  End If
 
  If sq + 16 <= 63 Then _
   If pos(sq + 8) > 0 Then _
    If pos(sq + 16) = 0 Then _
      jamp_piece start_sq, sq + 16
  
  If sq - 16 >= 0 Then _
   If pos(sq - 8) > 0 Then _
    If pos(sq - 16) = 0 Then _
      jamp_piece start_sq, sq - 16
    
  If (sq And 7) > 1 Then _
   If pos(sq - 1) > 0 Then _
    If pos(sq - 2) = 0 Then _
      jamp_piece start_sq, sq - 2
  
  If (sq And 7) < 6 Then _
   If pos(sq + 1) > 0 Then _
    If pos(sq + 2) = 0 Then _
      jamp_piece start_sq, sq + 2
   
  exist(sq) = 0
 End If
End Sub


Sub move_piece(sq As Integer)
  If sq + 8 <= 63 Then
   If pos(sq + 8) = 0 Then
    link_move sq, (sq + 8)
   End If
  End If
  If sq - 8 >= 0 Then
   If pos(sq - 8) = 0 Then
    link_move sq, (sq - 8)
   End If
  End If
  If (sq And 7) < 7 Then
   If pos(sq + 1) = 0 Then
    link_move sq, (sq + 1)
   End If
  End If
  If (sq And 7) > 0 Then
   If pos(sq - 1) = 0 Then
    link_move sq, (sq - 1)
   End If
  End If
End Sub

Sub generate(ByVal side As Integer)
  tree_cnt(ply + 1) = tree_cnt(ply)
  For j = 0 To 63
    If pos(j) = side Then
       move_piece (j)
       jamp_piece j, j
    End If
  Next j
End Sub




       
Private Sub show_pos()
  For j = 0 To 63
   If pos(j) = black_pawn Then
     frmMain.Image1(j).Picture = _
     frmMain.img_black_ch.Picture
   ElseIf pos(j) = white_pawn Then
     frmMain.Image1(j).Picture = _
     frmMain.img_white_ch.Picture
   Else
     frmMain.Image1(j).Picture = _
     frmMain.img_empty_sq.Picture
   End If
  Next j
End Sub



Private Sub cmdAbout_Click()
  frmAbout.Show (modal)
End Sub

Private Sub cmd3_Click()
  img_shape.Visible = False
  
  
  'play_file "d:\src\corners_vb\Eingebne.wav"
 
  
  end_piece_cnt(1) = 0
  end_piece_cnt(2) = 0
  
  start_piece_cnt(1) = 0
  start_piece_cnt(2) = 0
  
  static_score(1) = 0
  static_score(2) = 0
  
  For j = 0 To 63
   pos(j) = 0
   If init_pos(j) <> 0 Then
     insert_piece init_pos(j), j
   End If
  Next j
  show_pos
  
End Sub

Private Sub cmdNew_Click()
  Randomize
  img_shape.Visible = False
  
  end_piece_cnt(1) = 0
  end_piece_cnt(2) = 0
  
  start_piece_cnt(1) = 0
  start_piece_cnt(2) = 0
  
  static_score(1) = 0
  static_score(2) = 0
  
  For j = 0 To 63
   pos(j) = 0
   If init_pos(j) <> 0 Then
     insert_piece init_pos(j), j
   End If
   black_score_tbl(j) = Int(black_score_tbl_init(j) * Rnd)
  Next j
  show_pos
  'play_file "burg.wav"
End Sub

Private Sub Form_Load()
  init_pos = Array _
      (1, 1, 1, 0, 0, 0, 0, 0, _
       1, 1, 1, 0, 0, 0, 0, 0, _
       1, 1, 1, 0, 0, 0, 0, 0, _
       0, 0, 0, 0, 0, 0, 0, 0, _
       0, 0, 0, 0, 0, 0, 0, 0, _
       0, 0, 0, 0, 0, 2, 2, 2, _
       0, 0, 0, 0, 0, 2, 2, 2, _
       0, 0, 0, 0, 0, 2, 2, 2)
  click_sq(0) = 0
  click_sq(1) = 0
  
  black_score_tbl = Array _
      (1, 2, 3, 4, 5, 6, 7, 8, _
       2, 9, 10, 11, 12, 13, 14, 15, _
       3, 10, 16, 17, 18, 19, 20, 21, _
       4, 11, 17, 22, 23, 24, 25, 26, _
       5, 12, 18, 23, 27, 28, 29, 30, _
       6, 13, 19, 24, 28, 31, 32, 33, _
       7, 14, 20, 25, 29, 32, 34, 35, _
       8, 15, 21, 26, 30, 33, 35, 36)
 black_score_tbl_init = Array _
      (1, 2, 3, 4, 5, 6, 7, 8, _
       2, 9, 10, 11, 12, 13, 14, 15, _
       3, 10, 16, 17, 18, 19, 20, 21, _
       4, 11, 17, 22, 23, 24, 25, 26, _
       5, 12, 18, 23, 27, 28, 29, 30, _
       6, 13, 19, 24, 28, 31, 32, 33, _
       7, 14, 20, 25, 29, 32, 34, 35, _
       8, 15, 21, 26, 30, 33, 35, 36)
  
  target_tbl = Array _
  (-2, -2, -2, 0, 0, 0, 0, 0, _
   -2, -2, -2, 0, 0, 0, 0, 0, _
   -2, -2, -2, 2, 2, 2, 0, 0, _
    0, 0, 2, 2, 2, 2, 0, 0, _
    0, 0, 2, 2, 2, 2, 0, 0, _
    0, 0, 2, 2, 2, 60, 60, 60, _
    0, 0, 0, 0, 0, 60, 60, 60, _
    0, 0, 0, 0, 0, 60, 60, 60)
    
 xside(1) = 2
 xside(2) = 1
    
 cmdNew_Click
 
    
End Sub

Private Sub Image1_Click(Index As Integer)
 Dim L%, T%
 Dim find As Boolean
 
 
 If is_end_game > 0 Then
   MsgBox ("Game Over! Click 'New' or EXit")
   Exit Sub
 End If
 
 
 'frmMain.img_shape.Move(left,top)
 L = frmMain.Image1(Index).Left
 T = frmMain.Image1(Index).Top
 frmMain.img_shape.Left = L
 frmMain.img_shape.Top = T
 frmMain.img_shape.Visible = True
' frmMain.img_shape.Top = True
 click_sq(0) = click_sq(1)
 click_sq(1) = Index
 
 'user move ?
 If pos(click_sq(0)) = white_pawn Then
  If pos(click_sq(1)) = 0 Then
     
     generate white_pawn
     find = False
     For j = 0 To tree_cnt(1) - 1
       If tree(j).a = click_sq(0) Then
        If tree(j).b = click_sq(1) Then
         find = True
        End If
       End If
     Next j
     
     If find Then
       make_move click_sq(0), click_sq(1)
       
      ' un_make_move click_sq(0), click_sq(1)
              
       
       click_sq(0) = click_sq(1)
       show_pos
       DoEvents
              
       If is_end_game > 0 Then
          play_file "Eingebne.wav"
          MsgBox ("Congratulate! Game Over!")
       Else
       
         Screen.MousePointer = vbHourglass
         find_move = search_best(black_pawn)
         Screen.MousePointer = vbDefault
         
         If find_move Then
          make_move find_best(0).a, _
                    find_best(0).b
          show_pos
          DoEvents
          If is_end_game > 0 Then
            MsgBox ("Sorry! Game Over")
          End If
         Else
           MsgBox ("?! No moves found!")
         End If
         
       End If
       
     End If
  End If
 End If
End Sub


 

 

 

Игра в Дурня на Си под ДОС

Когда то давным давно я в отпуске купил компьютер ZX-Spectrum, подключил его к телевизору и о чудо, там была брошюрка по программированию на бейсике для данного компа. Причем, включая звук и прочие прибамбасы. Так я познакомился с условиями и циклами и за отпуск успел написать карточную игру в дурня для текстового режима. Играла программа строго по алгоритму. Ходила с наименьшей и подкидывала всегда не жалея. Там надо было только добавить случайный выбор приоритета в пределах разумного но как то это до меня не сразу дошло и много лет спустя уже на ватком Си я пытался прикрутить альфа-бета процедуру поиска (подобно полному перебору). Дурень как известно мастерам - игра комбинационная. Так родилась сия программка. Она сочетает Монте Карло случайный выбор хода с поискам лучшей линии для двух сторон (стороны по очереди ходят по алгоритму). Это конечо не совсем комбинационный стиль игры но все же.
Ссылка исходников и экзешника для DOSBOX. Можно компильнуть в текстовом режиме на Visual C. Не судите за графику. Что мог так сказать.
Заглавочный файл программы:

 

 #ifndef KARD_H
#define KARD_H
///////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <conio.h>
#include <sys/timeb.h>
#include <assert.h>
#include <limits.h>


#if INT_MAX==32767
  #define MS_DOS_REAL_MOVE  // comment this line
#endif
#if (_MSC_VER > 1200)
  #define kbhit _kbhit
  #define getch _getch
  #define getche _getche
 // #define sscanf sscanf_s
 // #define sprintf sprintf_s
#endif

enum Status{END_GAME=0, ZAHOD_COMP  =1, OTBOY_COMP  =2,
            ZAHOD_PLAYER=3, OTBOY_PLAYER=4};

enum Side{PLAYER,COMP,NOSIDE};
enum Mast{CHERVY,BUBY,KRESTY,PIKI};               //масть карты
enum Dost{K_6,K_7,K_8,K_9,K_10,K_J,K_D,K_K,K_T};  //достоинство карты
enum GameResult{DRAW=1, WINE=1000, LOSE=-1000};
#define NOKARD -1
#define NOMAST -1
#define KEY_EXIT  100 //user input
#define KEY_GO    101 //user input
#define dost(k) (k%9)   // return 0..8, '6'..'tuz'
#define mast(k) (k/9)   // return 0..3, 'chervy'..'piki'
extern char *dost_str[];
extern char *mast_str[];

struct Dec{
  int v[36],cnt;
};
struct Game{
  struct Dec player, comp, koloda, igra,
         koleso_fortuny;
  struct Dec *p[2];
  int kozyr;
  enum Status game_state;
  enum GameResult result[2];
  int is_load;
  int cnt_list_player_kard;
};
#define GAME_FILE_NAME "gamelst.dat"
#define MSG_HOD  "Pick the kard or press Enter('q'-exit)"
//"Enter the card number (0..31) or key-ENTER(retreat) or 'q' to quit"
#define MSG_LEFT "Do you want pass kard left?"
//"Do you want pass kard left? Card number (0..31) or key-ENTER"
//функции - предварительное описание
int new_game(void); int show_pos(void); int delay(void);
int razdacha(void); int check_end(void); int user_input(const char msg[]); int
iq_can_move_left(int kard,int side);
int shiftleft(struct Dec* x,int min,int max); int mics_dec(struct Dec *koloda);
int less_kard(struct Dec* z, int mast);int pick_kard(struct Dec *p, int kard);
int user_player_other_input(void); unsigned long gettickcount(void);
int run(void);
int find_kard(struct Dec *p, int kard); int find_dost(struct Dec *p, int dos);
int find_mast(struct Dec *p, int mast);
int is_kard_capture(int k0, int k1); char* kard_to_str(int kard,int N);
int clear_key_buf(void);int sort_kard(int v[], int sz);
enum Side getside(void);
enum Side opside(enum Side side);
int do_move(int k); int beru(void); int otboy(void);
int is_zahod(void); int proverka(void);
int get_first_kard_zahod_comp(int side);
int get_next_kard_zahod_comp(int side);
int end_game(void); int find_kard_otboy_comp(int k0,int side);
int get_first_kard_zahod_player(void);
int get_next_kard_zahod_player(void);
int find_kard_otboy_player(int kard0);
int zahod_player1(void); int otboy_player1(void);
int zahod_comp1(void); int otboy_comp1(void);
int save_game(void);
int load_game(void);
int get_other_kards_left_player(void);
int get_other_kards_left(void);
int compare_kard_order(int k0,int k1);
int get_other_kards_left_player_helper(void);
int get_cnt_starshih_kozyrey(int side);
int is_kards_bad(struct Dec *p);
int iq_check_zahod_kard_mid_game(int kard, int side);
int print_str_status(const char s[]);
int read_score(int c);
int Song(void); int Song1(void);
int show_game_result(void);
int get_koleso_fortuny();
////



//переменные игры
extern struct Game g, old_g;
extern int last_wine;
extern unsigned long diff; //задержка во времени в (1/18.2 sec)
extern int is_search; //? идет поиск
extern int is_text_mode;







////////////////
#endif

понедельник, 22 сентября 2025 г.

Игра Крестики-Нолики на java с графическим интерфейсом

 

 

 

Игра в Крестики-Нолики как известно уже бородатая задача для ИИ. Надо получить некоторую шустрость, но оставить игроку возможность выиграть. Я нашел прототип в инете (без поиска) и потерял ссылку, у кого увел графику:)) Перебор в общем идет на три полухода, оценка случайная в пределах разумного - каждая клетка имеет оценку как функцию от количества линий которые можно замкнуть с данного поля. Ссылка на исходник:

https://disk.yandex.ru/d/rnc8CABdtIXSUQ


/***
*  Простая игра в крестики-нолики
*  с графическим интерфейсом и выбором хода
*  с элементом случайности
*  evgeniy-korniloff@yandex.ru
***/

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

public class TicTacToeGame {
    public static void main(String args[]) {
        Gui gui = new Gui();

    }
}




class TicTacToe {
    public static int count = 0;
    public boolean test = true;
    public Gui gui;
    ////////////////////
    Random random;

    public static String[][] board = new String[3][3];
    public static XYS tbl[]=new XYS[5];

    final   int KREST=0,NIL=1,EMPTY=2,INF=1000;
    public  int xboard[][]=new int[3][3];
    public  int side=KREST;
    public int score_board[][]=new int[3][3];
    public static int score_board_max[][]={
     { 3, 2, 3},
     { 2, 4, 2},
     { 3, 2, 3}
    };
    
    public static int full_line[][]={
        {0,1,2},
        {3,4,5},
        {6,7,8},
        {0,3,6},
        {1,4,7},
        {2,5,8},
        {0,4,8},
        {6,4,2}
    };
    Boolean is_full_line(int side){
        for(int j=0;j<8;j++){
            int i;
            for(i=0;i<3;i++){
                int y=full_line[j][i]/3;
                int x=full_line[j][i]%3;
                if(xboard[y][x]!=side)
                    break;
            }
            if(i==3) return true;
        }
        return false;
    }
    int Evaluate(int c,int depth){
        if(is_full_line(c))return INF+depth;
        if(is_full_line(c^1)) return -INF - depth;
        int s[]={0,0};
        int cnt=0;
        for(int y=0;y<3;y++)
            for(int x=0;x<3;x++)
                if(xboard[y][x]!=EMPTY){
                    s[xboard[y][x]] += score_board[y][x];
                    cnt++;
                }
        return cnt==9? -1+random.nextInt(3):s[c] - s[c^1];
    }
    Boolean Draw(){
        int cnt=0;
        for(int y=0;y<3;y++)
            for(int x=0;x<3;x++)
                if(xboard[y][x]!=EMPTY){
                    cnt++;
                }
        return cnt==9? true:false;
    }
    void init_score(){
       for(int y=0;y<3;y++)
            for(int x=0;x<3;x++)
                score_board[y][x]=
                 random.nextInt(score_board_max[y][x]*3);
    }
    class XYS{
      public int x,y,score;
      void Clear(){x=-1;y=-1;score=-INF-10;}
      XYS(){Clear();}
      Boolean Empty(){return x==-1 || y==-1;}
    };
    public void call_search(){
      random = new Random(System.nanoTime());
      init_score();
      XYS z = new XYS();
      int t = search(side,3,z);
      if(z.Empty()==false){
        xboard[z.y][z.x]=side;
        if(side==KREST)
          gui.gboard[z.y][z.x].setText("X");
        else if(side==NIL)
          gui.gboard[z.y][z.x].setText("o");
        side ^= 1;
      }
    
    }
    public int search(int c,int depth,XYS z){
        z.Clear();
        XYS tmp = tbl[depth];

        int t = Evaluate(c,depth);
        if(depth<=0 || t<=-INF || t>=INF || Draw()) return t;
        for(int y=0;y<3;y++)
         for(int x=0;x<3;x++)
          if(xboard[y][x]==EMPTY)
           {
              xboard[y][x]=c;
              int e = -search(c^1,depth-1,tmp);
              if(e>z.score){
                z.score = e;
                z.x=x;
                z.y=y;
              }
              
              xboard[y][x]=EMPTY;
           }
         return z.score;
    }
    
    void new_game(){
        
        for(int y=0;y<3;y++)
            for(int x=0;x<3;x++)
            {
                xboard[y][x]=EMPTY;
            }
        side=KREST;
        init_score();        
    }
/// /////////////////////
    public TicTacToe(Gui i){
        gui=i;
        random = new Random(System.nanoTime());
        new_game();
        for(int j=0;j<5;j++)
          tbl[j]=new XYS();
        //if(random.nextInt(2)==1)
        //  call_search();
    }
    public int get_button_index_on_board(JButton button){
        for(int y=0;y<3;y++)
            for(int x=0;x<3;x++)
                if(gui.gboard[y][x]==button)
                    return y*3+x;
        return -1;
    }
    public void buttonClicked(JButton button) {
        if(side==KREST) {
          int i = get_button_index_on_board(button);
          if(i!=-1) {
             int y = i/3;
             int x = i%3;
             if(xboard[y][x]==EMPTY){
                xboard[y][x]=KREST;
                side=NIL;
                button.setText("X");
             }  
          }
        }else if(side==NIL){
            int i = get_button_index_on_board(button);
            if(i!=-1) {
                int y = i/3;
                int x = i%3;
                if(xboard[y][x]==EMPTY){
                
                    xboard[y][x]=NIL;
                    side=KREST;
                    button.setText("O");
                }
            }
        }
        call_search();

        /**
        if(test) {
            if(button.getText().equals("")) {
                count++;
                if(count % 2 == 1) {
                    button.setText("X");
                }
                if(count % 2 == 0) {
                    button.setText("O");
                }
            }
        }
         **/
    }

    public void gameRules(JButton button) {
       if(is_full_line(KREST) ||
           is_full_line(NIL)){
            
          JOptionPane.showMessageDialog(null, " won.");
          
        }else if(Draw()){
          JOptionPane.showMessageDialog(null, " draw.");            
            
        }
          
        /***
        if(test) {
     //   if(false){

            //"X" or "O"?
            String string = button.getText();

            //Gives coordinates of the button
            int x = Character.getNumericValue(button.getName().charAt(0));
            int y = Character.getNumericValue(button.getName().charAt(1));
            board[x][y] = string;

            if(board[0][0] != null && board[0][0].equals(board[1][1]) && board[1][1].equals(board[2][2])) {
                JOptionPane.showMessageDialog(null,string + " won.");
                test = false;
            }

            else if(board[0][2] != null && board[0][2].equals(board[1][1]) && board[1][1].equals(board[2][0])) {
                JOptionPane.showMessageDialog(null,string + " won.");
                test = false;
            }
            else if(count == 9) {
                JOptionPane.showMessageDialog(null, "draw.");
                test = false;
            }
            else {
                for (int i = 0; i < 3; i++) {
                    if (board[i][0] != null && board[i][0].equals(board[i][1]) && board[i][0].equals(board[i][2])) {
                        JOptionPane.showMessageDialog(null, string + " won.");
                        test = false;
                        break;
                    }
                    if (board[0][i] != null && board[0][i].equals(board[1][i]) && board[0][i].equals(board[2][i])) {
                        JOptionPane.showMessageDialog(null, string + " won.");
                        test = false;
                        break;
                    }
                }
            }
            
        }
        */
    }
}



class Gui {
    public         JButton[][] gboard = new JButton[3][3];

    public Gui() {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        TicTacToe ticTacToe = new TicTacToe(this);
        panel.setLayout(new java.awt.GridLayout(3, 3));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        for (int i = 0; i < 3; i++){
            for(int j = 0; j < 3; j++) {
                final JButton button = new JButton();
                gboard [j][i]=button;
                String string = i +  "" + j;
                button.setText("");
                button.setName(string);
                button.addActionListener(
                        new ActionListener() {
                            public void actionPerformed(ActionEvent e) {
                                ticTacToe.buttonClicked(button);
                                ticTacToe.gameRules(button);
                            }
                        }

                );
                button.setFont(new Font("Arial", Font.PLAIN, 40));
                button.setBorder(BorderFactory.createLineBorder(Color.BLACK));
                panel.add(button);
            }

        }

        frame.add(panel);
        frame.setSize(400,400);
        frame.setVisible(true);




    }
}

понедельник, 1 сентября 2025 г.

Библиотека классов mini-STL для программ на C++

 Библиотека классов для программ на C++.


 

Давненько я обдумывал по ходу какими могли бы быть классы для стека, строки, словаря и пр. и естественно, управление памятью и сборка мусора.

И тут как то   сел и написал симбиоз классов, стековых и требующих аллокатора. Основная идея, это что вместо конструктора используется функция производитель, деструктора нет, а удаление происходит всем скопом по стековому принципу mark-release, как в турбо пасскале. Вся библиотека в одном файле *.h. Это конечно, не заменить STL, но мне так удобнее, особенно для хеширования данных в быстрых программах.

 

скачать архив с исходниками 

 

#ifndef HASH_INC
#define HASH_INC
////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/****
 * Библиотека несложных классов с
 * аллокатором памяти
 * Stack
 * String
 * Vector
 * Hash
 * Dictionary
 * FreeArray
 * Utils - random hash keys
 * Управление памяти подобно
 * turbo pascal, классу Allocator
 * в начале передается максимальное
 * количество памяти и процедура
 * Mark() запоминает состояние а
 * Release() возвращает к последнему
 * Mark(), удаляяя мгновенно все обьекты
 * которые созданы после
 * evgeniy-korniloff@yandex.ru, 2025
 */
//стековый обьект
template <int T, class D>  
  class Stack{
   public:
    D data[T]; int top;
    Stack(){top=0;}
  D Push(D d) { assert(top<T);return (data[top++]=d); }
  D Pop(){assert(top>0);return data[--top]; }
  D Top(){assert(top>0);return data[top-1]; }
  bool Empty(){return top<=0; }
  };
/////////
//стековый обьект + куча
class Allocator{
 public:
  char*data;int size;int cnt;Stack<100,int> st;
  
  Allocator(int N=10000){
    data=(char*)malloc(N);size=N;cnt=0;
    assert(data!=NULL);
    memset(data,0,N);
  }
  void Mark(){ st.Push(cnt);}
  void Release(){ cnt=st.Pop();}
  void* GetMem(int N){
     void*q = (char*)&data[cnt]; 
     cnt+=N;
     if(cnt>size){
       fprintf(stderr,"allocator: not memory \n");
       abort();
     }
     return q;
  }
  void* GetTop(){ return (char*)&data[cnt]; }
  Allocator(){free(data);}
};
/////////////
//создается по указателю
class String{
 public:
  char *data; int N; Allocator *a;
 static String* Create(Allocator*a,const char* s){
   String*q = (String*)a->GetMem(sizeof(String));
   assert(s!=NULL);
   q->a = a;
   q->N=strlen(s);
   q->data=(char*)a->GetMem(q->N+1);
   strcpy(q->data,s);
   return q;
 }
 char* Append(const char*s){
   char buf[N]; strcpy(buf,data); 
   N += strlen(s);
   data=(char*)a->GetMem(N);
   strcpy(data,buf); 
   strcat(data,s);
   return data;
 }
 char* Get(){ return data; }
 int Len(){return N;}
};  
/////////////
//создается по указателю
template <class D>
class Vector{
 public:
  D *data; int N; Allocator *a;
 static Vector* Create(Allocator*a,
                       int N,
                       const D s[]){
   Vector*q = (Vector*)a->GetMem(sizeof(Vector));
   assert(s!=NULL);
   q->N = N;
   q->a = a;
   q->data=(D*)a->GetMem(q->N*sizeof(D));
   memcpy(q->data,s,q->N*sizeof(D));
   return q;
 }
 D* Append(int Cnt, const D s[]){
   int old_N=N;
   if(GetTop()==a->GetTop()){//dobavlyaem v konec
     N+=Cnt;
     a->GetMem(Cnt*sizeof(D));
     //??
     //printf("-->\n");
   }else{
       D buf[old_N]; memcpy(buf,data,old_N*sizeof(D)); 
       N+=Cnt;
       data=(D*)a->GetMem(N*sizeof(D));
       memcpy(data,buf,old_N*sizeof(D)); 
   }
   memcpy(&data[old_N],s,Cnt*sizeof(D));
   return data;
 }
 D* Append(const D s){
   return Append(1,&s);
 }
 
 D&   ElementAt(int i){ return data[i]; }
 int Len(){return N;}
 void* GetTop(){return &data[N];}
 void Sort(){ sort_sliyaniem(data,N);}
 
 
 protected:
 void swap(D v[],int a,int b){
     D t = v[a];v[a]=v[b];v[b]=t;
 }

 void sort_vibor(D v[],int n){
    int j,k,max_i; D max;
     for(j=0;j<n-1;j++){
       max=v[j]; max_i=j;  
       for(k=j+1;k<n;k++){
          if(v[k]>max){
             max=v[k];
             max_i=k;  
          } 
       }  
       if(max_i!=j) swap(v,j,max_i);
     }
 }
 void sort_sliyaniem(D v[], int n){
 if(n<6) sort_vibor(v,n);
 else{
   int j,i,q,w,e;
   int d = n/2+1;
   D a[d], b[d];
   for(j=0;j<d;j++) a[j]=v[j];
   for(j=d,i=0; j<n; j++,i++) b[i]=v[j];
   sort_sliyaniem(a,d);
   sort_sliyaniem(b,i);
   for(q=0,w=0,e=0;q<d && w<i;){
     if(a[q]>b[w]){
       v[e++] = a[q++];
     }else{
       v[e++] = b[w++];
     }
   }
   while(q<d) v[e++] = a[q++];
   while(w<i) v[e++] = b[w++];

 }
}
};  
//////////////////
typedef unsigned long long ULL;

static unsigned long int next_seed = 1; 
class Utils{
  public:
    
//случайные числа, всегда одинаковая последовательность
  static int rand()
  {
      
    next_seed = next_seed * 1103515245 + 12345;
    return (unsigned int)(next_seed / 65536) % 32768;
  } 
  static ULL rand64(){
    ULL A = rand();
    A <<= 15; // сдвиг на 15, так как 7FFF покрывает 15 бит
    A |= rand();
    A <<= 15;
    A |= rand();
    A <<= 15;
    A |= rand();
    A <<= 3;
    A |= rand() & 0b111; // дополнительные 3 случайных бита      
    return A;      
  } 
  static ULL ByteHashCode(unsigned char b){
     static int first=0;
     static ULL hash[256];
     if(first==0){
        first=1;
        for(int i=0;i<256;i++)
          hash[i]=rand64(); 
     } 
     return hash[b];      
  }
  static ULL GetHashKey(void*data,int N){
    unsigned char*p=(unsigned char*)data;
    int j=0; ULL w=0;
    while(j<N){
      w ^= ByteHashCode(p[j]);
      j++;
    }
    return w;
  }  
    
};
///////////////////////////////////

 class HKeyValue{
  public:
  void *data; int N; ULL hkey; Allocator*a;
  static HKeyValue* Create(Allocator*a,void*data,
                           int N, ULL hkey=0){
    HKeyValue*q=(HKeyValue*)a->GetMem(sizeof(HKeyValue));
    q->data=a->GetMem(N);
    memcpy(q->data,data,N);
    q->a=a;q->N=N;q->hkey=hkey;
    return q;
  }
 };
 ///////////////////////////////////////
 class iHash{
  public:
  class HNode{
   public:
    HKeyValue*key, *value; HNode*next;
    static HNode* Create(Allocator*a,
                         HKeyValue*key,
                         HKeyValue*value){
      HNode*q = (HNode*)a->GetMem(sizeof(HNode));                     
      q->key=key; q->value=value; q->next=NULL;
      return q;
    }
  };
  public:
    HNode**tbl; int N; Allocator*a;
  static iHash* Create(Allocator*a, int N=1000){
   iHash*q=(iHash*)a->GetMem(sizeof(iHash*));
   q->tbl = (HNode**)a->GetMem(N*sizeof(HNode*));
   q->a=a; q->N=N;
   return q;
  }
  int GetEntryIndex(ULL hkey){return hkey%N;}
  HKeyValue* Find(HKeyValue* key){
    int i = GetEntryIndex(key->hkey);
    HNode*q = tbl[i];
    while(q){
      if(q->key->hkey==key->hkey &&
         q->key->N==key->N &&
         memcmp(q->key->data,key->data,key->N)==0)
         return q->value;
      q=q->next;
    }
    return NULL;
  }
  void Insert(HKeyValue* key,HKeyValue* value){
    HNode*w = HNode::Create(a, key, value);
    int i = GetEntryIndex(key->hkey);                       
    w->next = tbl[i];
    tbl[i] = w;
    
  }
  static ULL GetHashKey(void*data,int N){
    unsigned char*p=(unsigned char*)data;
    int j=0; ULL w=0;
    while(j<N){
      w ^= ~(((ULL)p[j]) << (j%48));
      j++;
    }
    return w;
  }
 };
////////////////////////////  
class SDictionary{
 public:
  iHash* h; Allocator*a;
  char t[2][32];unsigned i;
 static SDictionary* Create(Allocator*a){
   SDictionary*q=(SDictionary*)a->GetMem(sizeof(SDictionary));
   q->h = iHash::Create(a,1000);
   q->a=a;
   q->t[0][0]=q->t[1][0]='\0';
   q->i=0;
   return q;
 }
 char* ITOA(int d){
   char *s=&t[(i++)%2][0];
   sprintf(s,"%d",d);
   return s;
 }
 int ATOI(char *s){
   return atoi(s);
 }
 //itoa(number, str, 10);
 void Insert(const char*key_,const char*value_){
    HKeyValue* key=HKeyValue::Create(a,(void*)key_,
                           strlen(key_)+1, 
                           iHash::GetHashKey( (void*)key_,strlen(key_))); 
    HKeyValue* value=HKeyValue::Create(a,(void*)value_,
                           strlen(value_)+1, 
                           0); 

    h->Insert(key,value);  
 }
 char* FindValue(const char *s){
    a->Mark();
    HKeyValue* key=HKeyValue::Create(a,(void*)s,
                           strlen(s)+1, 
                           iHash::GetHashKey( (void*)s,strlen(s))); 

    int i = h->GetEntryIndex(key->hkey);
    iHash::HNode*q = h->tbl[i];
    while(q){
      if(q->key->hkey==key->hkey &&
         q->key->N==key->N &&
         memcmp(q->key->data,key->data,key->N)==0)
      {
         a->Release();
         return (char*)q->value->data;
      }
      q=q->next;
    }
    a->Release();
    return NULL;
  }
 
};
//////////////
template <class D>
class FreeArray{
 public:
  iHash* h; Allocator*a;
static FreeArray* Create(Allocator*a,int Hash_Size=1000){
   FreeArray<D>*q=(FreeArray*)a->GetMem(sizeof(FreeArray<D>));
       //???
   // printf("stop  size=%d  cnt=%d\n",a->size,a->cnt);
   // getchar();
   
   q->h = iHash::Create(a,Hash_Size);

   q->a=a;
   return q;
 }
 D& ElementAt(D d){
 

    a->Mark();
    HKeyValue* key=HKeyValue::Create(a,(void*)&d,
                           sizeof(D), 
                           iHash::GetHashKey( (void*)&d,sizeof(D))); 

    int i = h->GetEntryIndex(key->hkey);
    iHash::HNode*q = h->tbl[i];
    while(q){
      if(q->key->hkey==key->hkey &&
         memcmp(q->key->data,key->data,key->N)==0)
      {
         a->Release();
         return *((D*)q->value->data);
      }
      q=q->next;
    }
    a->Release();
    
        
    HKeyValue* k,*v; D foo=0;
    k=HKeyValue::Create(a,(void*)&d,
                        sizeof(D), 
                        iHash::GetHashKey( (void*)&d,sizeof(D))); 
 
                            
    v=HKeyValue::Create(a,&foo,
                        sizeof(D), 
                        0); 

                        
    h->Insert(k,v);
    return *((D*)v->data);
  }
};
//////////////




/////////////////
#endif