Форум программистов, компьютерный форум CyberForum.ru

Программа для игры в покер - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 31, средняя оценка - 4.68
nefton
44 / 20 / 5
Регистрация: 28.02.2013
Сообщений: 184
28.02.2013, 13:15     Программа для игры в покер #1
Никак не могу понять суть ооп.
До прихода в с++ програмировал на С микроконтроллеры.
Там всё просто. Есть начало программы, и пишеш инструкции последовательно чтоб устройство работало так, как задумано. Используя регистры, прерывания и тп.

Сейчас же выучив немного С++ пытаюсь написать консольное приложение для игры в покер сам с собой.
Написал функцию сравнения рук в покере. Даже написал обьект "Автомат игры" для безлимитного холдема
для 2-10 чел. (переписывал много много раз)
Последняя редакция подкупает своей лаконичностью. В этом обьекте нет "представления" типа ников игроков, аватарок и тп. Нет функции рисования игры в консоле и ввода от пользователя ходов.

Вопрос как мне организовать программу чтоб было красиво (понятно) и расширяемо и тп. Вобщем как это делают нормальные програмисты?

Вот у меня есть main(). Это точка входа. (в визуал С++ я так её и не нашол когда пытался )
Далее по логике я создаю экземпляры классов какие мне надо в программе.

Что дальше? Бесконечный цикл ожидания когда пользователь нажмёт кнопку? И вызов соответствующих функций какихто классов?
Лучшие ответы (1)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Kuzia domovenok
 Аватар для Kuzia domovenok
1882 / 1737 / 116
Регистрация: 25.03.2012
Сообщений: 5,907
Записей в блоге: 1
01.03.2013, 23:36     Программа для игры в покер #21
Цитата Сообщение от XRuZzz Посмотреть сообщение
вы не сможите скомпилировать класс с_Figure на компиляторе для микроконтроллера
1) ТС не пользуется компилятором для МК (по крайней мере для своего покера).

Цитата Сообщение от XRuZzz Посмотреть сообщение
ну STL вы же сможете скомпилировать где угодно.
2) Об STL вообще речи не шло. Тем более о компиляции "где угодно". ("где угодно" это намёк на микроконтроллеры? - см п.1.)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
XRuZzz
01.03.2013, 23:45
  #22

Не по теме:

пожалуй заменю слово МК на Linux, чтоб не вводить людей в ступор. Хотя ТС-у наверно ближе МК
webio

nefton
44 / 20 / 5
Регистрация: 28.02.2013
Сообщений: 184
02.03.2013, 00:24  [ТС]     Программа для игры в покер #23
Цитата Сообщение от XRuZzz Посмотреть сообщение
#include "stdafx.h"
Это не моя прихоть, это мой компилятор так пишет. А namespace std не понимает.
Microsoft Visual C++ 2005 Expres Edition

Микроконтроллеры (точнее компиляторы для них) не поддержуют С++ и уж точно не поддерживают его полностью.
Програмирую на С.
ни namespace ни stdafx.h там не надо вовсе (нет никаких "стандартных" потоков ввода вывода), да и память считается в единицах килобайт и нельзя сувать туда всё подряд.
Контроллеры младших семейств не имеют даже команды простого умножения байта на байт.
И потому по привычке стараюсь использовать операции битового сдвига на С и избегаю деления и работы с плавающей точкой (хотя современному процессору вроде пофиг что делить или умножать всё равно 1 такт)

Добавлено через 8 минут
Проэлюстрирую вышесказанное моей функцией перевода руки в холдеме в число.
Тоесть сила каждой возможной руки однозначно определяется 1м числом типа long (4 байта)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
unsigned long deal::GetHand(char card1, char card2, char card3, char card4, char card5, char card6, char card7/*, char* opis*/){
// Преобразуем 5 карт в руку типа лонг (32 бита)
  //Младшие 13 бит заполняем наличием карт определённого достоинства
    /*Кодировка карт: карта представляет собой 1 байт (char)
  0,1,2,3 биты означают достоинство (2..Туза)
  2=1; 3=2; 4=3; 5=4; 6=5; 7=6; 8=7; 9=8; T=9; J=10; Q=11; K=12; A=13;
  4,5 биты означают масть
  в™*=0; в™Ј=1; ♦=2; ♥=3;*/
 
unsigned long otvet=0;
unsigned long temp1=0;
unsigned long temp2=0;
 
unsigned int spades=0;
unsigned int clubs=0;
unsigned int diamonds=0;
unsigned int hearts=0;
 
if(card1>>4==0)spades|=(1<<((card1&0xF)-1));
if(card1>>4==1)clubs|=(1<<((card1&0xF)-1));
if(card1>>4==2)diamonds|=(1<<((card1&0xF)-1));
if(card1>>4==3)hearts|=(1<<((card1&0xF)-1));
 
if(card2>>4==0)spades|=(1<<((card2&0xF)-1));
if(card2>>4==1)clubs|=(1<<((card2&0xF)-1));
if(card2>>4==2)diamonds|=(1<<((card2&0xF)-1));
if(card2>>4==3)hearts|=(1<<((card2&0xF)-1));
 
if(card3>>4==0)spades|=(1<<((card3&0xF)-1));
if(card3>>4==1)clubs|=(1<<((card3&0xF)-1));
if(card3>>4==2)diamonds|=(1<<((card3&0xF)-1));
if(card3>>4==3)hearts|=(1<<((card3&0xF)-1));
 
if(card4>>4==0)spades|=(1<<((card4&0xF)-1));
if(card4>>4==1)clubs|=(1<<((card4&0xF)-1));
if(card4>>4==2)diamonds|=(1<<((card4&0xF)-1));
if(card4>>4==3)hearts|=(1<<((card4&0xF)-1));
 
if(card5>>4==0)spades|=(1<<((card5&0xF)-1));
if(card5>>4==1)clubs|=(1<<((card5&0xF)-1));
if(card5>>4==2)diamonds|=(1<<((card5&0xF)-1));
if(card5>>4==3)hearts|=(1<<((card5&0xF)-1));
 
if(card6>>4==0)spades|=(1<<((card6&0xF)-1));
if(card6>>4==1)clubs|=(1<<((card6&0xF)-1));
if(card6>>4==2)diamonds|=(1<<((card6&0xF)-1));
if(card6>>4==3)hearts|=(1<<((card6&0xF)-1));
 
if(card7>>4==0)spades|=(1<<((card7&0xF)-1));
if(card7>>4==1)clubs|=(1<<((card7&0xF)-1));
if(card7>>4==2)diamonds|=(1<<((card7&0xF)-1));
if(card7>>4==3)hearts|=(1<<((card7&0xF)-1));
 
//Проверяем есть ли стрит-флеш
 
if (temp1=IsStreet(spades)){/*sprintf(opis,"straight-flush");*/ otvet=(8<<28)|temp1; return otvet;};
if (temp1=IsStreet(clubs)){/*sprintf(opis,"straight-flush");*/ otvet=(8<<28)|temp1; return otvet;};
if (temp1=IsStreet(diamonds)){/*sprintf(opis,"straight-flush");*/ otvet=(8<<28)|temp1; return otvet;};
if (temp1=IsStreet(hearts)){/*sprintf(opis,"straight-flush");*/ otvet=(8<<28)|temp1; return otvet;};
 
//Проверяем есть ли 4(каре)
if(temp1=spades&clubs&diamonds&hearts){
  temp2 = spades|clubs|diamonds|hearts;
  temp2&=~temp1;//гасим нашу 4ку
  char t;
  t = GetHiCard(temp2);
  otvet = (7<<28)|(temp1<<13)|t;
  //sprintf(opis,"4");
  return otvet;
}
 
unsigned int troiki=0;
troiki = (spades&clubs&diamonds)|(spades&clubs&hearts)|(spades&diamonds&hearts)|(clubs&diamonds&hearts);
unsigned int dvoiki=0;
dvoiki = (spades&clubs)|(spades&diamonds)|(spades&hearts)|(clubs&diamonds)|(clubs&hearts)|(diamonds&hearts);
 
//Проверяем есть ли 3+2
if (troiki){
  //находим старшую тройку
  char t;
  t = GetHiCard(troiki);
  //ишем двойки
  dvoiki&=~(1<<t);//гасим в двойках старшую тройку (нестаршие тройки остаются)
  if (dvoiki){
    otvet = (6<<28)|(t<<20)|dvoiki;
    //sprintf(opis,"3+2");
    return otvet;
  }
}
//Проверяем есть ли flash
char s=0,c=0,d=0,h=0;
for(int i=0;i<=12;i++){
  if(spades&(1<<i))s++;
  if(clubs&(1<<i))c++;
  if(diamonds&(1<<i))d++;
  if(hearts&(1<<i))h++;
}
if(s>=5){
    otvet = (5<<28);
    char t;
    t = GetAndRemoveHiCard(&spades);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&spades);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&spades);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&spades);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&spades);
    otvet|=(1<<t);
    //sprintf(opis,"flush");
    return otvet;
}
if(c>=5){
    otvet = (5<<28);
    char t;
    t = GetAndRemoveHiCard(&clubs);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&clubs);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&clubs);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&clubs);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&clubs);
    otvet|=(1<<t);
    //sprintf(opis,"flush");
    return otvet;
}
if(d>=5){
    otvet = (5<<28);
    char t;
    t = GetAndRemoveHiCard(&diamonds);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&diamonds);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&diamonds);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&diamonds);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&diamonds);
    otvet|=(1<<t);
    //sprintf(opis,"flush");
    return otvet;
}
if(h>=5){
    otvet = (5<<28);
    char t;
    t = GetAndRemoveHiCard(&hearts);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&hearts);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&hearts);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&hearts);
    otvet|=(1<<t);
    t = GetAndRemoveHiCard(&hearts);
    otvet|=(1<<t);
    //sprintf(opis,"flush");
    return otvet;
}
 
//Проверяем есть ли стрит
if(temp1 = IsStreet(spades|clubs|diamonds|hearts)){otvet=(4<<28)|temp1;/* sprintf(opis,"straight");*/ return otvet;}
 
//Проверяем есть ли 3
if (troiki){//если мы дошли сюда, то у нас может быть только 1 тройка
  otvet = (3<<28)|(troiki<<13);
  unsigned int r = spades|clubs|diamonds|hearts;
  r&=~troiki;//гасим тройку
  char t;
  t = GetAndRemoveHiCard(&r);
  otvet|=(1<<t);
  t = GetAndRemoveHiCard(&r);
  otvet|=(1<<t);
  t = GetAndRemoveHiCard(&r);
  otvet|=(1<<t);
  //sprintf(opis,"3");
  return otvet;
}
 
 
//Проверяем есть ли 2+2 или 2
if (dvoiki){
  
  unsigned int r = spades|clubs|diamonds|hearts;
  char ost;
  //находим старшую двойку
  char t;
  t = GetAndRemoveHiCard(&dvoiki);
  if (dvoiki){//если есть другие двойки кроме найденной
    char t2;
    t2 = GetAndRemoveHiCard(&dvoiki);
    r&=~(1<<t);//гасим старшую двойку
    r&=~(1<<t2);//гасим младшую двойку
    ost = GetAndRemoveHiCard(&r);
    otvet = (2<<28)|(t<<24)|(t2<<20)|ost;
    //sprintf(opis,"2+2");
    return otvet;
  }
  otvet = (1<<28)|(t<<24);
  r&=~(1<<t);//гасим двойку
  ost = GetAndRemoveHiCard(&r);
  otvet|=(1<<ost);
  ost = GetAndRemoveHiCard(&r);
  otvet|=(1<<ost);
  ost = GetAndRemoveHiCard(&r);
  otvet|=(1<<ost);
  //sprintf(opis,"2");
  return otvet;
}
 
//Если ничего нет (старшая карта)
unsigned int r = spades|clubs|diamonds|hearts;
char ost;
otvet = (0<<28);
ost = GetAndRemoveHiCard(&r);
otvet|=(1<<ost);
ost = GetAndRemoveHiCard(&r);
otvet|=(1<<ost);
ost = GetAndRemoveHiCard(&r);
otvet|=(1<<ost);
ost = GetAndRemoveHiCard(&r);
otvet|=(1<<ost);
ost = GetAndRemoveHiCard(&r);
otvet|=(1<<ost);
//sprintf(opis,"Hi card");
return otvet;
}
Становится понятно что в этом коде без пол литры не расберёшся (не смотроя на обильный коментарий)
Но эта функция - одно из произведений за которое я реально горд.
Вот она будет работать даже на самом маленьком контроллере и очень очень быстро
Kuzia domovenok
 Аватар для Kuzia domovenok
1882 / 1737 / 116
Регистрация: 25.03.2012
Сообщений: 5,907
Записей в блоге: 1
02.03.2013, 00:56     Программа для игры в покер #24
Цитата Сообщение от nefton Посмотреть сообщение
Это не моя прихоть, это мой компилятор так пишет. А namespace std не понимает.
Microsoft Visual C++ 2005 Expres Edition
в настройках созданного проекта это можно отключить(отключить precompiled headers). А чтобы студия изначально не создавала stdafx нужно создавать в ней проект как empty project.

Добавлено через 2 минуты
Цитата Сообщение от nefton Посмотреть сообщение
/*Кодировка карт: карта представляет собой 1 байт (char)
* 0,1,2,3 биты означают достоинство (2..Туза)
* 2=1; 3=2; 4=3; 5=4; 6=5; 7=6; 8=7; 9=8; T=9; J=10; Q=11; K=12; A=13;
* 4,5 биты означают масть
* ♠=0; ♣=1; ♦=2; ♥=3;*/
Эта экономия на спичках в МК может быть ещё и оправдана с учётом его ограниченной памяти, но вообще для такого используют структуры и не парятся. Впрочем, если очень хочешь поэкономить на битах существуют битовые поля.

Добавлено через 9 минут
Согласись, так понятнее.
C
1
2
3
4
5
6
7
8
enum CSuit{
Spades=0,Clubs,Diamonds,Hearts
};
 
struct card{
 enum CSuit suit;
 int number;
};
в прочие бесконечные стены текста даже не пытался вникнуть. Открой для себя циклы что ли.
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,265
02.03.2013, 01:04     Программа для игры в покер #25
Цитата Сообщение от nefton Посмотреть сообщение
Вот она будет работать даже на самом маленьком контроллере и очень очень быстро
а зачем?
nefton
44 / 20 / 5
Регистрация: 28.02.2013
Сообщений: 184
02.03.2013, 01:05  [ТС]     Программа для игры в покер #26
я хорошо знаком с циклами
в данном конкретном случае побитовый подход оправдал себя на все 100%
и дело вовсе не в пару байтах памяти.

А в принципе. Принципиально используя структуры не получится уместить силу руки в 1 число.
Прийдётся делать классы классы классы, и в конечном итоге некий класс руки и перегружать для него операторы сравнения.

В итоге для определения победителя из 6ти человек уйдёт в 100 (не преувеличиваю ни капли) раз больше времени у процессора(современного) чем при моей функции. А в покере чтоб посчитать чтото методом монтекарло это очень очень очень критично.

Для несовременного процессора вся функция будет выполнена быстрее раз в 10 чем поделить 2 целых числа друг на друга. и раз в 100 быстрее чем поделить 2 числа с плавающей точкой.
Kuzia domovenok
 Аватар для Kuzia domovenok
1882 / 1737 / 116
Регистрация: 25.03.2012
Сообщений: 5,907
Записей в блоге: 1
02.03.2013, 01:19     Программа для игры в покер #27
Цитата Сообщение от nefton Посмотреть сообщение
А в принципе. Принципиально используя структуры не получится уместить силу руки в 1 число.
http://habrastorage.org/storage/habr...b24b43b9f3.jpg

Добавлено через 1 минуту
Цитата Сообщение от nefton Посмотреть сообщение
используя структуры не получится уместить силу руки в 1 число.
битовые поля. слышал про такое?
nefton
44 / 20 / 5
Регистрация: 28.02.2013
Сообщений: 184
02.03.2013, 01:29  [ТС]     Программа для игры в покер #28
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
битовые поля. слышал про такое?
Слышал.
- не уверен что будет более читаемо
- не уверен что компилятор не поступит с моими записями какимто извращённым способом
(и буду потом искать где алгоритм тормозит пол жизни)
- я даже не уверен что там есть операторы сдвига.

Да и вопрос зачем????? Ведь оператор сдвига ничем не хуже чем сложение, вычиитание, деление.
мало того, он даже появился в програмировании раньше
Зачем пытатся имитировать работу простейших операторов (проще только инкримент) с помощью битовых полей и тп.

Это всё равно что пытатся придумать свою реальзацию оператора сложения для целых чисел. как то через класс целого числа
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,265
02.03.2013, 01:35     Программа для игры в покер #29
Я повторю свой вопрос- а зачем? На фига эти все выкрутасы? Какая разница уйдёт на расчёт миллиард лет или 100 миллиардов?
Kuzia domovenok
 Аватар для Kuzia domovenok
1882 / 1737 / 116
Регистрация: 25.03.2012
Сообщений: 5,907
Записей в блоге: 1
02.03.2013, 01:46     Программа для игры в покер #30
Цитата Сообщение от nefton Посмотреть сообщение
Зачем пытатся имитировать работу простейших операторов (проще только инкримент) с помощью битовых полей и тп.
пока что я вижу, что ты имитируешь работу структур и битовых полей с помощью своих выкрутасов.
троллейбус_из_буханки.жпг

Добавлено через 5 минут
Цитата Сообщение от nefton Посмотреть сообщение
- не уверен что будет более читаемо
ой, правда?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
if(card1>>4==0)spades|=(1<<((card1&0xF)-1));
if(card1>>4==1)clubs|=(1<<((card1&0xF)-1));
if(card1>>4==2)diamonds|=(1<<((card1&0xF)-1));
if(card1>>4==3)hearts|=(1<<((card1&0xF)-1));
 
if(card2>>4==0)spades|=(1<<((card2&0xF)-1));
if(card2>>4==1)clubs|=(1<<((card2&0xF)-1));
if(card2>>4==2)diamonds|=(1<<((card2&0xF)-1));
if(card2>>4==3)hearts|=(1<<((card2&0xF)-1));
 
if(card3>>4==0)spades|=(1<<((card3&0xF)-1));
if(card3>>4==1)clubs|=(1<<((card3&0xF)-1));
if(card3>>4==2)diamonds|=(1<<((card3&0xF)-1));
if(card3>>4==3)hearts|=(1<<((card3&0xF)-1));
 
if(card4>>4==0)spades|=(1<<((card4&0xF)-1));
if(card4>>4==1)clubs|=(1<<((card4&0xF)-1));
if(card4>>4==2)diamonds|=(1<<((card4&0xF)-1));
if(card4>>4==3)hearts|=(1<<((card4&0xF)-1));
 
if(card5>>4==0)spades|=(1<<((card5&0xF)-1));
if(card5>>4==1)clubs|=(1<<((card5&0xF)-1));
if(card5>>4==2)diamonds|=(1<<((card5&0xF)-1));
if(card5>>4==3)hearts|=(1<<((card5&0xF)-1));
 
if(card6>>4==0)spades|=(1<<((card6&0xF)-1));
if(card6>>4==1)clubs|=(1<<((card6&0xF)-1));
if(card6>>4==2)diamonds|=(1<<((card6&0xF)-1));
if(card6>>4==3)hearts|=(1<<((card6&0xF)-1));
 
if(card7>>4==0)spades|=(1<<((card7&0xF)-1));
if(card7>>4==1)clubs|=(1<<((card7&0xF)-1));
if(card7>>4==2)diamonds|=(1<<((card7&0xF)-1));
if(card7>>4==3)hearts|=(1<<((card7&0xF)-1));
читабельнее чем
C++
1
2
3
4
5
for (i=0; i<7; i++)
   switch(card[i].suit){
          case spades: ...
....
}
или что ты там хотел сказать?
nefton
44 / 20 / 5
Регистрация: 28.02.2013
Сообщений: 184
02.03.2013, 01:52  [ТС]     Программа для игры в покер #31
Ну смотря что считать. Например если даны карманные карты 4х игроков, они все алин и надо посчитать % выигрыша каждого или мат ожидание выигрыша. Для нормальной точности надо скажем 10000 раздач.
Обычный покерный калькулятор. У меня на эту операцию (из жизни) уходит менее 1с.
Ждать 100с мало кто станет если это онлайн калькулятор к примеру.
Я пока не могу обосновать на 100% что это очень критично, но в любом случае быстрее лучше чем медленнее.
Видел много других реализаций сравнения рук. Скажу что они никак не более читабельны.

Добавлено через 3 минуты
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
for (i=0; i<7; i++)
* *if(card[i].suit==spades)
или что ты там хотел сказать?
Ну вот прочёл же, а говоришь не читабельно

В этом моменте да, можно написать короче.
AnyOne697
 Аватар для AnyOne697
134 / 106 / 5
Регистрация: 22.05.2010
Сообщений: 532
02.03.2013, 03:13     Программа для игры в покер #32
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
тут тоже
В принципе верно, но не совсем. Достаточно большой проект принято разделять и влавствовать чуть ли не до бесконечности. То есть по сути появляется большое количество модулей разделённых относительно кода (но не работы - они могут работать и параллельно). У каждого модуля есть своя "точка входа" и своя "точка выхода".

Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
И к регистрам и прерываниям никто тебе доступа не даст, когда параллельно с тобой запущено ещё несполько приложений и сама операционка Windows.
Наглая ложь! Я хоть и пару месяцев кодил на асме, но что Linux, что Windows даёт полный доступ к любым прерываниям и регистрам. Как же другие приложения? Насколько я помню, регистров не мало + запоминаются состояния. К тому же шанс того, что система тебя вытолкнет из работы вне вызова какого-нибудь прерывания довольно мал, так что в принципе содержимому в регистрах можно доверять. И именно поэтому во времена Pentium II-III-IV винда могла очень легко "залипнуть".

Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
Ну в принципе да, а что? Методы классов - те же функциии. Вообще всё зависит, например, оконное у тебя приложение или консольное.
Ну в принципе да, на практике - иначе. ЯВУ позволяют обойтись без... такого цикла
C
1
for (;;) {...break;...}
То есть где-нибудь есть что-то вроде
C++ (Qt)
1
return app.exec()
который в неявном виде запускает цикл и настраивает events или систему сообщений, уведомлений, сигнал-слотов. По-разному...
Но на самом деле, это именно цикл. В цикле программа методично превреяет, не были нажаты определённые кнопки и как на их нажатие реагировать (изменять ход течения программы).
Цитата Сообщение от kravam Посмотреть сообщение
И я не сразу понял. Да и понял ли? Для меня ООП- возможность конструировать собственные типы данных
Вот-вот... Мне вообще кажется, что в этом мире только от силы полтора человека понимают суть ООП. Остальные умеют лишь пользоваться им...
ООП - совокупность методов, методик построения ПО. Нельзя взять и понять ООП. Для этого, имхо, может потребоваться десятки лет практики и тонны прочитанной литературы. Но если грубо, то во времена WWII не было даже ассемблера и всё кодилось прямо в двоичных (максимум - десятиричных) кодах. Приход ассемблера внёс первый уровень абстракции - мнемоники. Потом парни из AT&T решили создать хорошую ОС. Но это было сложно и вдобавок они сделали Си. В общем-то, первый ужасный и в то же время очень мощный системый ЯВУ. Хотя, скорее ЯСУ (среднего уровня), но не суть - это всё мелочи. Впоследствии, ОС'ы становились более громозкими, их API разрасталось, усложнялось, расслаивалось; ПО стало более комплексное и куда более сложное... Си стало не хватать и появился Си++ - улучшенный Си, на что намекает инкремент, или Си с классами в раннем варианте. И если сначала комманды были представлены цифрами, потом их обозвали буквами, в Си их стало возможным объединить в функции, то в Си++ класс - аггрегатор функций на основе используемых данных! Это самая обычная эволюция (есть неизвестная революция - функциональные ЯП, но... слишком она неизвестная).

Ну вот, собственно и ответ. Как организовывать программу? Всё просто - пишем коды. Когда видим, что функция стала слишком большой, делаем больше функций. В идеале функции должны быть длиной 10-15 символов (хотя краткость - сестра таланта, не забываем), а размером - 5-6 строк максимум (содержательных, офк).
Так же и с классами - видем, что он ожирел - время рефакторинга. Разделяем сначала данные на классы (семантически, офк), после чего переписываем методы классов.
Из вредных советов, имхо:
+ должна быть структурка (или класс) GameState, массив этих GameState'ов и указатель на текущий. У них должны быть методы, вроде updateState()...
+ игрок человек и игрок компьютер не должны быть разделены классом - это одна сущность и не стоит плодить их по напрасну, лучше проверять тип внутри update() и либо ждать ввода, либо ходить самому каким-то образом.
+ бумага - лучший друг человека; она всё стерпит, а если писать карандашом - долго прослужит... хотя если есть комманда, white-board всё же лучше.


Это всё мнение человека (кроме исторических фактов, офк) и оно не претендует быть истинной в первой инстанции.

Добавлено через 2 минуты
Алсо, рекомендую [перевод].

Добавлено через 10 минут
Цитата Сообщение от nefton Посмотреть сообщение
Микроконтроллеры (точнее компиляторы для них) не поддержуют С++ и уж точно не поддерживают его полностью.
Не знаю о чём Вы, но Си++11 сейчас вообще никто полностью не поддерживает, а Си++ начала двухтысячных прекрасно транслируется в Си99 код. Тем более, что древние компиляторы именно так и делали Си++ -> Си -> ассемблер -> бинарник.

По поводу алгоритма? Что за сила руки? Есть вполне определённые алгоритмы рассчёта вероятности выигрыша. Нужно всего лишь посмотреть на свою руку, посчитать (читай, найти) лучшую комбинацию (что очень легко считается с помощью деревьев) и определить вероятность, что у противника рука слабее (с рассчётом на вылетевшие карты, размер колоды и возможности на флопе, тёрне и ривере получить что-нибудь интересное.
Это не так просто с точки зрения математики, но с точки зрения кода это довольно просто.

Добавлено через 6 минут
Цитата Сообщение от XRuZzz Посмотреть сообщение
вы не сможите скомпилировать класс с_Figure на компиляторе для микроконтроллера пока не уберете строчку
C
1
#include "stdafx.h"
Простите, что!? Причём здесь невозможность откомпилировать на микро- контроллер или процессор и прекомпилированные хэдеры?
Kuzia domovenok
 Аватар для Kuzia domovenok
1882 / 1737 / 116
Регистрация: 25.03.2012
Сообщений: 5,907
Записей в блоге: 1
02.03.2013, 03:52     Программа для игры в покер #33
Цитата Сообщение от AnyOne697 Посмотреть сообщение
Наглая ложь! Я хоть и пару месяцев кодил на асме, но что Linux, что Windows даёт полный доступ к любым прерываниям и регистрам. Как же другие приложения? Насколько я помню, регистров не мало + запоминаются состояния. К тому же шанс того, что система тебя вытолкнет из работы вне вызова какого-нибудь прерывания довольно мал, так что в принципе содержимому в регистрах можно доверять. И именно поэтому во времена Pentium II-III-IV винда могла очень легко "залипнуть".
к регистрам да. К прерываниям. Ну попробуй запиши что-нибудь по вектору прерываний реального процессора. Да вообще хоть к какому-то реальному устройству получи доступ под Windows.

Добавлено через 1 минуту
Цитата Сообщение от AnyOne697 Посмотреть сообщение
Простите, что!? Причём здесь невозможность откомпилировать на микро- контроллер или процессор и прекомпилированные хэдеры?
и какой компилятор для микроконтроллеров их использует?

Добавлено через 2 минуты
Цитата Сообщение от AnyOne697 Посмотреть сообщение
Микроконтроллеры (точнее компиляторы для них) не поддержуют С++ и уж точно не поддерживают его полностью.
Не знаю о чём Вы, но Си++11 сейчас вообще никто полностью не поддерживает, а Си++ начала двухтысячных прекрасно транслируется в Си99 код. Тем более, что древние компиляторы именно так и делали Си++ -> Си -> ассемблер -> бинарник.
При чём тут компиляторы Си++ для микроконтроллеров? Они вообще существуют такие? Не, может и есть, кто знает, но как-то я не встречал их пока.

Добавлено через 1 минуту
Цитата Сообщение от AnyOne697 Посмотреть сообщение
WWII
это ещё что?
XRuZzz
Антикодер
577 / 478 / 23
Регистрация: 15.09.2012
Сообщений: 2,429
02.03.2013, 12:00     Программа для игры в покер #34
Я видел мощные проекты на C++ для cortex в Keile. Я конечно не вникал особо в чем там ограничения для C++, но мне это никак не мешало в работе.

Тут нужно религиозно верить, либо не верить в ООП(C++).

Просто те исходники, которые я писал на C мне никак не пригодились в будующем.(может коряво писал)
AnyOne697
 Аватар для AnyOne697
134 / 106 / 5
Регистрация: 22.05.2010
Сообщений: 532
02.03.2013, 12:10     Программа для игры в покер #35
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
и какой компилятор для микроконтроллеров их использует?
Ничего сложного в precompiled headers нет и это зависит только от компилятора. Все современные компиляторы поддерживают такие заголовки, впрочем, насчёт микроконтроллеров не знаю, проекты там может быть и небольшие и библиотек мало, так что не знаю...
и какой компилятор для микроконтроллеров их использует?
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
При чём тут компиляторы Си++ для микроконтроллеров? Они вообще существуют такие? Не, может и есть, кто знает, но как-то я не встречал их пока.
Если честно, то не знаю. Знаю, что можно (можно было в какой-то степени) транслировать Си++ код в Си. Отсюда, я так думаю, можно и в микроконтроллер запихать Си++ проект. Правда если микроконтроллер 8-ми битный, то есть шанс поймать проблем.


Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
Цитата Сообщение от AnyOne697 Посмотреть сообщение
WWII
это ещё что?
World War II, вестимо.

Добавлено через 4 минуты
Цитата Сообщение от XRuZzz Посмотреть сообщение
Тут нужно религиозно верить, либо не верить в ООП
Вот только не надо начинать холивар. С ООП проще поддерживать большие проекты, а Си быстрее (впрочем, не намного) и даёт больший контроль над компьютером (или микроконтроллером). Да и вообще тема пошла в оффтопик =(
XRuZzz
Антикодер
577 / 478 / 23
Регистрация: 15.09.2012
Сообщений: 2,429
02.03.2013, 12:28     Программа для игры в покер #36
приведу пример своего класса Figures
файл #include "AsteriskFigures.h"
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef ASTERISKFIGURES_H_
#define ASTERISKFIGURES_H_
 
#include "IAsteriskFigures.h"
#include "ILineParts.h"
#include "HollowLineFoundations.h"
#include "BlackLineFoundations.h"
#include "LineParts.h"
#include "CenterLineParts.h"
#include "math.h"
 
namespace deitel
{
 
class AsteriskFigures: public IAsteriskFigures
{
    public:
        AsteriskFigures();
        virtual ~AsteriskFigures();
        string getLinePart(ILineParts * pTypeLine);
        int getType() const;
        void setType(int type);
        int getXmax() const;
        void setXmax(int xmax);
        int getYmax() const;
        void setYmax(int ymax);
    protected:
        int x;
        int y;
        LineParts *pLine;
        CenterLineParts cntrLine;
        int stepCnt;
    private:
        int type;
        int Xmax;
        int Ymax;
};
 
}
#endif
файл #include "AsteriskFigures.cpp"
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "AsteriskFigures.h"
#include "HollowLineFoundations.h"
 
namespace deitel
{
 
AsteriskFigures::AsteriskFigures()
{
    setXmax(9);
    setYmax(9);
    pLine = &cntrLine;
    stepCnt = 1;
}
 
AsteriskFigures::~AsteriskFigures()
{
}
 
int AsteriskFigures::getType() const
{
    return type;
}
 
void AsteriskFigures::setType(int type)
{
    this->type = type;
}
 
int AsteriskFigures::getXmax() const
{
    return Xmax;
}
 
void AsteriskFigures::setXmax(int xmax)
{
    Xmax = xmax;
}
 
int AsteriskFigures::getYmax() const
{
    return Ymax;
}
 
void AsteriskFigures::setYmax(int ymax)
{
    Ymax = ymax;
}
 
/**
 */
string AsteriskFigures::getLinePart(ILineParts * pTypeLine)
{
    string data;
    HollowLineFoundations hollow;
    data = pTypeLine->getLinePart(&hollow, 9);
    return data;
}
 
}
Конечно код не идеален, но он зависит только от моих интерфейсов и от стандартных библиотек.
NEbO
583 / 451 / 49
Регистрация: 22.01.2009
Сообщений: 1,173
Записей в блоге: 1
Завершенные тесты: 1
03.03.2013, 03:05     Программа для игры в покер #37
Сообщение было отмечено автором темы, экспертом или модератором как ответ
По поводу начала темы вставлю свои пять копеек. Может я несколько неправильно понял, но в самом топике автор говорит не о сложности самого ООП, а о сложности разработки GUI-приложений. Исхожу из того, что автору все-таки нужно добиться результата, и судя по опыту программирования микроконтроллеров, разобраться в том, что и как происходит.
Abstract
Почему? Вообще, когда я увидел название топика, я подумал, что какой-то [n]-классник в очередной раз пытается написать бота к покерстарзу/паразиду/леону/титану/ftt/ge/другое (подчеркнуть нужное, что там сейчас популярно?). Ну и сообщение в духе
Здравствуйте, я слышал, что для искусственного интеллекта применяются ГА и НС. Скажите, пожалуйста, что это такое? A еще: на каком языке лучше написать?
UPD:я вот знаю паскаль, я так понимаю, нужно завести два массива, в одном должен быть массив карт, в другом -- массив игроков? да? так?
ну итд итп, конечно. Однако, посмотрел на количество сообщений и все же заглянул посмотреть
Имхо, основной вопрос nefton-а заключается в последней строчке:
Цитата Сообщение от nefton Посмотреть сообщение
Что дальше? Бесконечный цикл ожидания когда пользователь нажмёт кнопку? И вызов соответствующих функций какихто классов?
Программист-системщик разбирается в написании удобного пользовательского интерфейса: это, на самом деле, очень интересно. И системное программное обеспечение, и прикладное, и разработка и даже проектирование UI: все интересно. Ну а то, что старые подходы не меняются, и хочется досконально разобраться в том, как все устроено, а не тупо(?) делать так, как предлагает компилятор/система/IDE/платформа: это возводит в квадрат интерес в написании развернутого (насколько смогу) ответа.
Возможно, конечно, я щас лишь напугаю, или оттолкну чем-то, в интересе изучения всего этого, хотя может оно и не нужно никому. Не знаю. Но все-таки я постараюсь объяснить принципы, по которым строится и как работает GUI-приложение, насколько удалось разобраться в этом мне. Ну и, если повезет, постараюсь намекнуть, почему разработку GUI и ООП вообще реально перепутать.
Поскольку буков многа, а я скромный по натуре, думаю, всем будет лучше, если я все это дело засуну под спойлер.
Кликните здесь для просмотра всего текста

Intro
Начну с того, что любое GUI-приложение можно разработать на чистом си, без плюсов. Да какой там си. Ассемблер тоже годится! Достаточно скачать FASM (http://flatassembler.net/), и заглянуть в примеры, чтобы убедиться, что вполне себе рабочий GUI можно писать даже на нем.
// Сразу скажу, что с иксами непосредственно я не работал, и на низком уровне писал только на WinAPI. Поэтому отталкиваюсь от него. В иксах же, как известно, добавляется еще и клиент/серверное взаимодействие, и, наверное, еще что-то, так что модель WinAPI, пожалуй, самый простой вариант.
Пробуем написать приложение, выводящее форму на экран и делает что-то при нажатии на левую кнопку мыши.
Начало стандартное:
C
1
2
3
int main() {
    return 0;
}

Не по теме:

заранее извиняюсь за K&R, знаю, что бОльшая часть сишников уважают ANSI-стиль


Ну и что же теперь? Известно, что при нажатии мышки возникает некоторое прерывание, обработчик которого, разумеется, находится в ОС. ОС также занимается обработкой прерываний при перемещении мышки, поэтому она точно может знать, в какой позиции экрана находится курсор. То есть при нажатии клавиши мышки, ОС сама по себе запросто определяет, какая клавиша была нажата (берется с аппаратуры), и позиция курсора.
Чтобы понять, на каком элементе, и в каком окне произошло нажатие, нужно, чтобы ОС знала, где располагаются эти элементы. Для этой цели ОС должна предоставлять функции регистрации некоторых прямоугольников, которые будут чем-то вроде "областей нажатия": при нажатии мышки на этом прямоугольнике, система должна запоминать, что в этом прямоугольнике клавиша была нажата.
Поэтому, определим структуру:
C
1
2
3
4
struct Rect {
    int x, y, width, height; // заполняются нашим приложением: оно как бы говорит операционке, что я хочу находиться там-то и там-то и иметь такой-то размер
    char isClicked; // флаг, который устанавливает система, когда произошло нажатие по этому прямоугольнику. пока что отбросим вопрос о том, кто и когда его должен сбрасывать
}
В ОС определим массив указателей на эти Rect-ы. Чтобы приложение и ОС оперировали одними и теми же Rect-ами, введем функцию регистрации Rect-а в ОС. Вызывать эту функцию должно приложение. Пусть оно изначально задает размер окна, мы же не хотим, чтобы наша псевдо-ОС всегда создавала прямоугольники одинаковых размеров?
Наглядное применение нашего "свежесозданного" апи, с применением for( ; ; ):
C
1
2
3
4
5
6
7
8
9
10
11
12
int main() {
    struct Rect rect;
    rect.x = 0; rect.y = 0; rect.width = 50; rect.height = 50; 
    rect.isClicked = 0; // хотя эту операцию может вызывать и ОС, при регистрации, так будет даже правильней
    registerRect(&rect);
    for ( ; ; ) {
        if (rect.isClicked) {
            // произошло нажатие. выполняем какие-то действия
        }
    }
    return 0;
}
И такой код даже будет работать! Но приложение будет занимать 100% ресурсов хотябы одного ядра (хотя, при гипертрединге, даже не знаю как правильно назвать, потока чтоли). Это очевидно. отбросим все до и после цикла: в цикле банальная проверка переменной на истинность. Причем, от того, что rect -- структура, код более волшебным не становится. На уровне ассемблера, в лучшем случае, это будет:
Assembler
1
2
3
4
5
6
7
mov  ebx, _rect + 12    ; смещение внутри структуры Rect до поля isClicked
_next:
    mov  al, [ebx]
    test al, al
    jz   _next
    ...                 ; тут наш код обработки нажатия
    jmp  _next
Как же избавиться от этой загрузки процессора?
В книге Таненбаума "Операционные системы. Разработка и реализация" упоминалось, что у каждого процесса есть некий флаг загруженности. То есть если программа была прервана операционной системой (кстати, делается это по таймеру, или IRQ0, при вытесняющей многозадачности), то ей нетрудно догадаться, что программа то еще работает! С другой стороны, если программа сама передала управление в ОС, сказав: "щас мне нечего делать. я жду ввода", ОС не зачем вхолостую тратить драгоценные такты на десятки, да или сотни даже таких процессов.
Значит, после создания Rect-а, программа должна передать управление в ОС специальным образом, и тогда флаг занятости будет установлен в 1, а планировщик не будет передавать управление нашей программе. При этом, из самой очереди планировщика ее можно не исключать, ведь ОС в любой момент может сбросить флаг занятости, и тогда ему нужно будет вновь передавать управление в нашу программу.

Не по теме:

Будь благословенен каждый, кто дочитал до сюда и все понял.


Как жеж такое сделать?
На самом деле все просто до безумия. Если подумать, когда жеж должно передаваться управление в программу, если та ожидает, что пользователь нажмет на мышку в окошке? Правильно: тогда, когда пользователь нажмет на мышку в этом окошке! А от мышки управление, как уже говорилось, поступает непосредственно к ОС: возникает некое прерывание. точно также от клавиатуры (для PS/2 это IRQ1), точно также при любом движении мышки/сенсорника, или любого другого устройства ввода (и не только). при этом, ОС отдает управление некоторому системному процессу.

Не по теме:

Это необходимо, т.к. сами прерывания должны обрабатываться моментально, ибо когда обрабатывается одно прерывание, по-хорошему, не может возникнуть другое. В итоге, скажем, если реализовать логику поиска среди всех зарегистрированных прямоугольников на экране именно в обработчике прерывания от мышки, то в это время будут теряться все "сообщения" от клавиатуры и от других устройств, вплоть до жесткого диска, который тоже вызывает некоторый IRQ в начале/конце записи/чтения блока данных. С учетом того, что логика поиска нужного прямоугольника по заданной точке может занять не один десяток килотактов, то может "потеряться" приличное количество прерываний.


Системный процесс реализует алгоритм поиска точки внутри заданного массива прямоугольников (все они зарегистрированы в системе, и этот системный процесс имеет доступ по крайней мере, на чтение этого массива). Определяет нужный прямоугольник, и .... Что? Правильно! Нужен некоторый идентификатор процесса, к которому привязываются все создаваемые в нем прямоугольники! А еще лучше, если у каждого прямоугольника будет указатель на процесс, ну или хотябы его идентификатор.
Так. Теперь уж точно хватит прямоугольников. Прямоугольник, имеющий какой то непонятный идентификатор на что-то вообще левое -- разве это прямоугольник? Назовем эту нашу штуку, имеющую размер, позицию, и ID процесса, окном (Window).

Не по теме:

На самом деле, в винапи, да я думаю, и в других подобных апи, определяется абстракция "приложение" (Application), к которому можно привязать n-ое количество этих окон. Window-ы привязываются к Application-у, а тот, уже, в свою очередь, связан с некоторым процессом в системе.


После того, как системный процесс определил нужное окно, он считывает из его структуры id процесса и сбрасывает его флаг занятости. Планировщик через какое-то время передает управление этому процессу. Процесс обрабатывает нажатие мышки и снова должен вызвать функцию ожидания клика, или завершиться. Получаем код типа:
C
1
2
3
4
5
6
7
8
9
10
11
12
int main(){
    struct Window wnd;
    wnd.x = 0; wnd.y = 0; wnd.width = 100; wnd.height = 100;
    wnd.pid = getpid(); // некоторая функция определения идентификатора текущего процесса.
    registerWindow(&wnd); // регрстрируем окно в системе
    for ( ; ; ) {
        waitForClick(&wnd); // ожидаем клик в окне. управление передается в ОС, она сбрасывает флаг занятости процесса wnd->pid
        ...
        // сюда передается управление, когда в окне был сделан клик
    }
    return 0;
}
Все, приложение больше не виснет, и обрабатывает клики как положено. Несмотря на бесконечный for( ; ; ).
Остаются "сущие пустяки": разобраться, как можно одновременно обрабатывать и клики мышки, и нажатия клавиш на клавиатуре, и какие-нибудь таймеры, "прокрутку" "колесиком" ну и прочее и прочее...
Я как-то уже упоминал слово "событие". Так вот. Всем этим нажатиям, движениям, прокруткам, таймерам, можно дать одно название: все это является событием (Event). Все эти события берутся от операционной системы. А откуда они берутся в операционной системе, было описано выше, на примере с кликом. События бывают разные, и описать его, например, так:
C
1
2
3
4
struct Event {
    int pos_x;
    int pos_y;
}
не получится, ибо при нажатии на кнопочку несколько непонятно, зачем передавать координаты мыши. Зато связать с этим событием нажатую клавишу, пожалуй, более чем желательно. Что делать? Конечно, есть замечательный способ: все запихать в одну кучу, и клавишу, и координаты мыши, и, скажем, смещение колесика мыши, и контекст рисования (да-да, события могут поступать не только от аппаратуры, но еще и от самой ОС. Например, как раз когда нам нужно наше окошко "отрисовать", ОС передает управление окну, ибо только оно знает, как оно должно выглядеть) и еще кучу всего. Но память при этом будет использоваться неэффективно. Можно попытаться сделать какие-нибудь union-ы, но в итоге мы все равно наверняка придем к достаточно сложным структурам
Кликните здесь для просмотра всего текста
Пример все с той же клавиатурой: при нажатии на клавишу передается только сама клавиша. На самом деле этого мало. Хотя, в принципе, конечно, этого достаточно, но удобнее, если нажатие по Ctrl или Alt или Shift не вызывало никаких событий, а всего лишь меняло бы некоторые флаги. А вот уже при нажатии на букву или цифру(например), мы получили бы целое, "большое" сообщение: с битовой маской зажатых клавиш-модификаторов. Также, CapsLock-и, ScrollLock-и, и NumLock-и должны автоматически менять нажатую клавишу внутри операционки, а в сообщении должна быть указана уже измененная клавиша.

Поэтому тут логично применить наследование (да-да, то самое, из ООП). Варианты возможной реализации я нашел тут: http://stackoverflow.com/questions/1...heritance-in-c. Но здесь я намеренно не буду их употреблять, ибо и так уже многа букав:
C
1
2
3
4
5
6
7
8
9
10
11
12
struct MouseEvent {
    int pos_x;
    int pos_y;
    int key_modifiers;
    int button;
};
struct KeyboardEvent {
    int key;
    int scanCode;
    int key_modifiers;
};
...
Конечно, я забыл рассказать о том, как передавать все эти события в приложение. Хотя, я думаю, если уж дочитали до сюда, то скорее всего, уже у вас есть какие-то мысли по этому поводу. Вот один из простейших вариантов, на основе очереди сообщений (инспайред бай винапи). События -- это частный случай сообщения. Например, так можно реализовать возможность посылки сообщений не только от системы, но и от окна другому окну, или даже самому себе.
  • Для начала убедимся, что у нас есть id текущего активного окна системы, activeWindowId, которое является глобальным. Также как в любой оконной среде вы можете выбрать только одно окно, и оно будет "основным", то есть, по умолчанию, события клавиатуры поступают к нему. Аналогично можно реализовать поведение "посылать события в окно под курсом": нам просто понадобится найти и сделать текущим окно под курсором в случае, когда пришло любое событие от клавиатуры или от мыши.
  • добавляем в структуру Window еще три поля:
    C
    1
    2
    3
    4
    5
    6
    
    struct Window {
        int x, y, width, height;
        int pid;
        list *messages;
        int window_id;
    }
    новые поля last_evt, last_evt_type, window_id должны заполняться ОС. Причем последний является уникальным глобальным идентификатором внутри всей системы. Это поле заполняется при регистрации окна.
  • В ядре системы добавим следующие структуры и функции:
    C
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    
    list *messagesQueue; // глобальный список событий
     
    struct Message {
        int wnd; // id окна, которому предназначается сообщение
        int type; // тип сообщения. служит только для того, чтобы правильно прочитать структуру message
        void *message; // тело сообщения, например, это может быть экземпляр структуры KeyboardEvent или MouseEvent
    }
     
    void sendMessage(int window_id, int message_type, void *message) { // message -- указатель на одну из структур, MouseEvent или KeyboardEvent и т.д. 
        struct Message msg;
        msg.wnd = window_id;
        msg.type = message_type;
        msg.message = message;
        list_append(messagesQueue, msg);
    }
     
    void process_events() { // код, исполняющийся в системном процессе, о котором я говорил раньше
        struct Message buf;
        while (list_unshift(messagesQueue, &buf)) { // извлечение из списка событий первого события. 
            // Эта операция должна быть атомарной, либо нужно ставить синхронизацию. 
            // Критическая секция возникает при возникновении прерывания во время удаления элемента из списка. 
            // Или, может, не возникает?? (добавление в конец, извлечение из начала. причем, при пустом списке сюда не войдет)
            // В любом случае, это всего лишь опытный образец, слава богу:)
            struct Window *wnd = getWindowById(buf.wnd);
            ... // вот тут как раз может идти обработка события: добавление и обработка различных флагов, модификаторов и прочей дребедени
                // также, сообщение можно не посылать, а запоминать где-то, напимер, для определения двойного щелчка мыши или тех же модификаторов
            list_append(wnd.messages, buf); // добавляем обработанное событие в очередь окна. 
                // Эта операция не должна выполняться для некоторых сообщений: например, как винда, Ctrl+Alt+Del обрабатывает сама, а окну сообщение с такой комбинацией не приходит.
            setProcessActive(wnd.pid, 1); // важно также установить флаг активности процесса, которое получило сообщение. Получило, значит, ему есть что обрабатывать. Пусть сделает это
        }
    }
     
    void waitForMessages(struct Window *wnd) { // ожидание сообщений
        setProcessActive(wnd->pid, 0); // сбрасываем флаг активности процесса. собствнно, все, пожалуй
    }
  • обработчики irq (аппаратные прерывания) пусть выглядят примерно таким образом:
    C
    1
    2
    3
    4
    5
    6
    
    __declspec(naked) void irq1_handler(int ch) { // событие от клавиатуры, на входе -- считанный с портов клавиатуры и преобразованный в "нормальный" вид скан-код нажатой клавиши
        struct KeyboardEvent *evt = malloc(sizeof(KeyboardEvent));
        evt->scanCode = ch;
        sendMessage(activeWindowId, EVT_KEYBOARD, evt); 
        asm("iretd"); // хотя, там еще надо APIC сбросить, ага, я помню, пару операций записи в порты. только не помню, в какие, да и сейчас на smp и x64 могло все поменяться, поэтому врать не буду
    }

Не по теме:

Разумеется, код упрощен до безумия. Смысл его только в том, чтобы показать принцип, на самом низком уровне, доступном программисту. Как оно от аппаратуры приходит "в жизнь", ну приблизительно. Не учтены по крайней мере переходы из ring0 в ring3, и наоборот, еще __declspec(naked) не поддерживает, скажем, gcc под x86 (и x86_64, разумеется, тоже). хотя как-то мною был сделан на 70% рабочий патч, исправляющий это. Кроме того, я никогда не находил информации о том, как в действительности работать с мышью. Видел только несколько реализаций на основе int 33h, но разумеется, тут это не прокатит, хотя бы потому что это софтвеерное досовское прерывание доки по юсб не осилил, ну а 2ой ps/2 не нашел из каких портов читать. в этом я, разумеется, каюсь, но на сами принципы это никак не влияет



Затем, с этими событиями и обработкой сообщений все становится крайне просто:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int main(){
    struct Window wnd;
    int finished = 0;
    wnd.x = 0; wnd.y = 0; wnd.width = 100; wnd.height = 100;
    wnd.pid = getpid(); // некоторая функция определения идентификатора текущего процесса.
    registerWindow(&wnd); // регрстрируем окно в системе
    while (!finished) { // основной цикл обработки сообщений
        waitForMessage(&wnd); // вот она, волшебная функция!
        struct Message msg;
        while (list_unshift(wnd.messages, &msg)) { // читаются все поступившие сообщения из очереди
            switch (msg.type) {
            case MSG_CLICK: // один из видов MouseEvent: клик в окне
                MouseEvent *evt = (MouseEvent*)msg.message;
                if (evt->button == MOUSE_BUTTON_LEFT) {
                    setWindowTitle(&wnd, "Mouse Left Button Clicked");
                }
                break;
            case MSG_DBLCLICK: // двойной клик
                closeWindow(&wnd); // система вычищает все лишнее, связанное с этим окном и посылает последнее сообщение, "перед смертью" окна: MSG_EXIT
                break;
            case MSG_EXIT:
                finished = 1;
                break;
            }
        }
    }
    return 0;
}
Примерно такой же подход, только с утроенным количеством кода, можно найти в любом хеллоуворлде на си для винапи. Конечно, где-то наверняка можно найти документацию по каждой из функций, которая там применяется, и имея относительно небольшой опыт и желание понять, как все это устроено, до всего вышеописанного можно дойти самому. Но, надеюсь, я своим постом хоть кому-то смог донести, как работают волшебные "Application.ProcessMessages; Application.DispatchMessage; Application.TranslateMessage" в делфях, что находится внутри qt-шной app.exec(), а также его механизм сигналов и слотов (они вставляются в этот основной цикл обработки сообщений), ну и что, на самом низком уровне, скрыто за main() в MFC, да и, впрочем, любой гуи-шной либы.

Вот. ну а по поводу библиотек разработки, помоему у qt самый низкий порог вхождения, плюс собственная неплохая гуи, плюс кроссплатформенность. На нем же, пожалуй, хорошо учиться основным паттернам проектирования. Впрочем, теми же качествами обладает и wxWidgets, но он немного посложнее сам по себе, и его, говорят, сложнее портировать на некоторые платформы. Из плюсов wx: огромное количество поддерживаемых языков (в т.ч. луа, хаскель и эрланг, к примеру). Visual Studio тяжелый, работает только под винду, из достоинств разве что отладка поприятнее в чем-то. Если вдруг понадобится распространять приложение, то у qt LGPL лицензия (нельзя изменять саму библиотеку и при этом не открывать код. при динамической компоновке код своего приложения можно никогда никому не показывать и продавать до тех пор, пока налоговая не возмет за одно место), у wx -- BSD подобная (делай вообще все что хочешь, желательно только лишь положить текст самой лицензии в папку с приложением, хотя и это не очень-то и обязательно). Ну а VS работает со своими runtime библиотеками, устанавливающимися на целевой машине, отдельно. там разумеется лицензий 0, так как попросту лицензировать нечего, тем более что компилятор бесплатный.

По поводу расширяемости программы. Нужно так прикинуть, насколько оно должно быть расширяемо (ну например, если это только для того, чтобы играть с другими игроками, по сети, на одном столе, и расширять нужно только всякие там карты, анимации движения, -- это одно. или все же может захотеться когда-нибудь сделать возможность игры МТТ, -- это другое). Нужно это для того, чтобы определить API (Application Programming Interface: тот функционал (интерфейс), который будет доступен плагинам) программы. Есть технологии плагинов с разделяемыми библиотеками и скриптовые. В первом случае на тех же плюсах создается длл-ка, которой при инициализации передается некий объект, содержащий реализацию функций API. Она этот указатель на этот объект сохраняет, во внутренней переменной, а потом им пользуется, когда нужно что-то сделать в контексте основной программы. Для полного щастья для разделения интерфейса/реализации API используется паттерн "мост", либо банально массив(или сложная структура) указателей на функции (неужели после первой части звучит ужасающе? вот пример, как это делается). Во втором случае, программа содержит в себе реализацию небольшой виртуальной машины для скриптового языка. При этом она (программа) также передает ей (виртуальной машине) некий объект, содержащий реализацию того, что можно сделать с основной программой. машина регистрирует у себя этот объект под некоторым именем (в самих виртуальных машинах чуть менее чем всегда используются строки, для именования переменных, это все сильно резко упрощает). Далее, когда основная программа решила, что пора бы уже поработать плагину, она дает ВМ (виртуальной машине) соответсвующее указание: мол запусти-ка скриптик из такого-то файла. и та запускает, и этому скрипту разумеется, передается то самое API в том самом глобальном объекте, которым всегда ему можно будет воспользоваться, чтобы совершить действия с основной программой (пример).
По первому варианту все ясно с деталями реализации (плюсы, компиляция, dll/so). По второму: реализаций ВМ полно. По большому счету, кроме синтаксиса языка, они различаются только производительностью. Так вот, в этом плане наиболее удачные, на данный момент, реализации -- это Google V8 и LuaJIT. Есть и еще несколько, если синтаксис этих ну совсем не катит

Не по теме:

приведенные ссылки -- это именно встраиваемые языки, то есть относительно легко прикручивающиеся к другим программам. в действительности, их тысячи


Для сведения, Google V8 наоптимизировали по самое небалуйся, и результат сопоставим по скорости даже с тем же си, в вычислительных задачах (!!!), по крайней мере на одном ядре. Хотя никто не мешает все трудоемкие операции перенести в плюсы, тем более, что раз уж на нем пишется приложение, это будет сделать очень и очень просто. Так что, я бы в первую очередь при выборе скриптового языка обратил бы внимание на то, насколько мне понравился его синтаксис, и насколько интересно мне его будет изучать (если придется).

По поводу алгоритма подсчета вероятности на вскрытии. Когда-то сам думал, что эта задача решается только моделированием. Но вот только пока сочинял эту хрень, я придумал следующий алгоритм.
Весь алгоритм делится на две возможности:
  • после флопа. 3 карты уже открыты, а значит, осталось перебрать всего лишь (54 - 3 - 2*2) * ( (54 - 3 - 2*2) - 1 ) варианта всех возможных раскладов (для количеечтва игроков = 2 останется наибольшее количество возможных карт) в худшем случае. Это всего лишь 2162 итерации. На каждой итерации прибавляем по 1 очку игроку(ам), которые выйграли, а после всего делим количество этих очков обратно на 2162 (точнее, на 54 - 3 - N*2, где N -- количество игроков. Ну я думаю, это элементарно, если вдуматься), получая количество положительных исходов на общее их число (я не силен в теории вероятностей, но все-таки умножив на 100, вроде получим количество процентов, верно?).
  • на префлопе. Таблицы. Заранее просчитанные. Еще раз напоминаю, что я не силен в тервере, однако я думаю, можно практически точно определить вероятность победы игрока A против игроков B и C, когда известны вероятности победы A против B, A против C и B против C. Я попробую как нибудь решить систему уравнений, но сейчас мозг плохо соображает, что там еще можно добавить... <аттеншен>я реально не сильно хорошо разбираюсь в тервере. если это невозможно, пожалуйста, придумайте алгоритм для решения этой, на мой взгляд, уже сильно упрощенной задачи</аттеншен>
  • рекомендации по представлениям. Каждую комбинацию из 5ти карт можно представить в виде 54*53*52*51*50 карт, что составляет 379 501 200 комбинаций. То есть вполне можно уложить в обычный int. Также, учтем, что условная сила руки (это с учетом "прикупа", ну то есть как его там.. кикера. во) будет не больше, чем это число. ну никак, верно? то есть тоже int. Тогда, если делать сервер для такой игры,где все должно просчитываться со сложностью O(1), можно составить табличку на каждую комбинацию (ну, в виде массива, конечно), где A=B. A -- смещение, B -- сила. и оба -- инты.

    Не по теме:

    Даже не думайте воспроизводить это на 64 битных системах!!! используте short int, все равно все уложится в него


    тогда табличка будет занимать в памяти 379501200 * 4 + 379501200 * 4 = 1518004800 * 2 = 3036009600. т.е. примерно 3 гига памяти. щас по 16-32 устанавливают, даже на не очень больших серверах, так что вполне можно справиться.
    Ну а если не нужна константая сложность, то пожалуйста, просчет, на каждый вариант. Правда, по-моему, это очень много тактов. Не уверен, что уложится в десятилетие секунду, хотя, возможно, я и неправ.
  • еще рах подумать над задачей о префлопе. Остальное перебором спокойно решается, я это уже показал. А вот с префлопом... хз. ну, придумать можно, что нибудь, я уверен! (даже не почти, я реально уверен... интуиция!!!)

Conslusion
Вообще, лично я бы на вашем месте подумал о реализации всего этого дела на html5+javascript(canvas). Недавно как раз познакомился с Google Closure Library, крутая штука. Производительность у canvas очень неплохая (ибо аппаратное ускорение, в большинстве современных браузеров), и есть несколько неплохих библиотек для него (пока что мне больше всех понравился fabric.js, хотя в той же GCL есть Canvas, пока еще с ним не разобрался). Хотя, скорее всего, можно обойтись для начала и без canvas-а, там и так можно сделать прикольную анимацию, и кучу красивостей. А сервер бы реализовал на эрланге. Но это конечно, только лишь мое личное предпочтение, ибо я, по большей части, web-программист, хоть и не фронтэндер (пхп-шник, разумеется). Просто прельщает возможность, скажем, зайти на сайт, и ничего не устанавливая, поиграть в онлайне с кем угодно.

Не по теме:

так, стоп, разве такого еще нет? гуглить стремно, не хочу, ато вдруг получится что зря все писал


NEbO
583 / 451 / 49
Регистрация: 22.01.2009
Сообщений: 1,173
Записей в блоге: 1
Завершенные тесты: 1
03.03.2013, 03:17     Программа для игры в покер #38
Черт. примеры-то забыл!
Простая реализация плагинной системы через шаред-либрариз: attach1.zip
Реализация через ВМ скриптового языка: attach2.zip
Для запуска без перекомпиляции требуются рантайм либы Qt5 (QtCore5.dll, QtScript5.dll, QtGui5.dll, QtWidgets5.dll) и mingwwm[чета там].dll. Должно компилиться под 4ую версию.
Не включил их потому что не понимаю, зачем их запускать. главное код. просто убедиться что все работает, разве что можно.
Еще, если интересна вдруг станет эта тема, могу более подробно объяснить, в картинках (uml диаграммы), как и что работает в общем случае, просто надо отыскать свой диплом первый
Вложения
Тип файла: zip attach1.zip (36.3 Кб, 4 просмотров)
Тип файла: zip attach2.zip (33.4 Кб, 3 просмотров)
XRuZzz
03.03.2013, 12:21
  #39

Не по теме:

это стоило оформить в качестве статьи(в каком нибудь блоге например), и в этой теме дать на неё ссылку, а теперь если аналогичный вопрос будет задан на киберфоруме или на другом сайте, придётся давать ссылку на эту тему.
и не факт, что человек найдёт статью в теме.
в общем хорошо зарыли ценный клад

MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
03.03.2013, 12:41     Программа для игры в покер
Еще ссылки по теме:

Теория игр на примере С3 из ЕГЭ по информатике. Программа, которая выдает стратегии для игры C++
C++ Программа поверх игры

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
nefton
03.03.2013, 12:41  [ТС]     Программа для игры в покер
  #40

Не по теме:

У каждого поста вроде есть ссылка.


Не по теме:

Попробую как то накидать схему приложения (в фотошопе???), а то можно обобщать обобщать и в конечном итоге всегда один и тот же вопрос - в чём смысл жизни

Yandex
Объявления
03.03.2013, 12:41     Программа для игры в покер
Ответ Создать тему
Опции темы

Текущее время: 10:38. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru